Merge branch 'kb/msvc-compile'

* kb/msvc-compile:
  Windows: do not redefine _WIN32_WINNT
  MinGW: Fix stat definitions to work with MinGW runtime version 4.0
  MSVC: fix stat definition hell
  MSVC: fix compile errors due to macro redefinitions
  MSVC: fix compile errors due to missing libintl.h
diff --git a/Documentation/RelNotes/1.7.11.2.txt b/Documentation/RelNotes/1.7.11.2.txt
index a0d24d1..f0cfd02 100644
--- a/Documentation/RelNotes/1.7.11.2.txt
+++ b/Documentation/RelNotes/1.7.11.2.txt
@@ -31,7 +31,7 @@
  * "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.
+   claimed that the tree-ish 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
diff --git a/Documentation/RelNotes/1.8.4.1.txt b/Documentation/RelNotes/1.8.4.1.txt
new file mode 100644
index 0000000..806545a
--- /dev/null
+++ b/Documentation/RelNotes/1.8.4.1.txt
@@ -0,0 +1,50 @@
+Git v1.8.4.1 Release Notes
+========================
+
+Fixes since v1.8.4
+------------------
+
+ * Some people still use rather old versions of bash, which cannot
+   grok some constructs like 'printf -v varname' the prompt and
+   completion code started to use recently.  The completion and
+   prompt scripts have been adjusted to work better with these old
+   versions of bash.
+
+ * "git rebase -i" had a minor bug (the same could be in other
+   programs, as the root cause is pretty generic) where the code
+   feeds a random, data dependeant string to 'echo' and expects it
+   to come out literally.
+
+ * "submodule.<name>.path" variable mistakenly set to the empty
+   "true" caused the configuration parser to segfault.
+
+ * Output from "git log --full-diff -- <pathspec>" looked strange,
+   because comparison was done with the previous ancestor that
+   touched the specified <pathspec>, causing the patches for paths
+   outside the pathspec to show more than the single commit has
+   changed.
+
+ * The auto-tag-following code in "git fetch" tries to reuse the
+   same transport twice when the serving end does not cooperate and
+   does not give tags that point to commits that are asked for as
+   part of the primary transfer.  Unfortunately, Git-aware transport
+   helper interface is not designed to be used more than once, hence
+   this did not work over smart-http transfer.  Fixed.
+
+ * Send a large request to read(2)/write(2) as a smaller but still
+   reasonably large chunks, which would improve the latency when the
+   operation needs to be killed and incidentally works around broken
+   64-bit systems that cannot take a 2GB write or read in one go.
+
+ * A ".mailmap" file that ends with an incomplete line, when read
+   from a blob, was not handled properly.
+
+ * The recent "short-cut clone connectivity check" topic broke a
+   shallow repository when a fetch operation tries to auto-follow
+   tags.
+
+ * On platforms with fgetc() and friends defined as macros,
+   the configuration parser did not compile.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.5.txt b/Documentation/RelNotes/1.8.5.txt
index 49a1c6c..ac5c3fa 100644
--- a/Documentation/RelNotes/1.8.5.txt
+++ b/Documentation/RelNotes/1.8.5.txt
@@ -48,6 +48,14 @@
 
 Foreign interfaces, subsystems and ports.
 
+ * "git-svn" used with SVN 1.8.0 when talking over https:// connection
+   dumped core due to a bug in the serf library that SVN uses.  Work
+   it around on our side, even though the SVN side is being fixed.
+
+ * On MacOS X, we detected if the filesystem needs the "pre-composed
+   unicode strings" workaround, but did not automatically enable it.
+   Now we do.
+
  * remote-hg remote helper misbehaved when interacting with a local Hg
    repository relative to the home directory, e.g. "clone hg::~/there".
 
@@ -63,6 +71,62 @@
 
 UI, Workflows & Features
 
+ * Instead of typing four capital letters "HEAD", you can say "@" now,
+   e.g. "git log @".
+
+ * "git check-ignore" follows the same rule as "git add" and "git
+   status" in that the ignore/exclude mechanism does not take effect
+   on paths that are already tracked.  With "--no-index" option, it
+   can be used to diagnose which paths that should have been ignored
+   have been mistakenly added to the index.
+
+ * Some irrelevant "advice" messages that are shared with "git status"
+   output have been removed from the commit log template.
+
+ * "update-refs" learnt a "--stdin" option to read multiple update
+   requests and perform them in an all-or-none fashion.
+
+ * Just like "make -C <directory>", "git -C <directory> ..." tells Git
+   to go there before doing anything else.
+
+ * Just like "git checkout -" knows to check out and "git merge -"
+   knows to merge the branch you were previously on, "git cherry-pick"
+   now understands "git cherry-pick -" to pick from the previous
+   branch.
+
+ * "git status" now omits the prefix to make its output a comment in a
+   commit log editor, which is not necessary for human consumption.
+
+ * Make "foo^{tag}" to peel a tag to itself, i.e. no-op., and fail if
+   "foo" is not a tag.  "git rev-parse --verify v1.0^{tag}" would be
+   a more convenient way to say "test $(git cat-file -t v1.0) = tag".
+
+ * "git branch -v -v" (and "git status") did not distinguish among a
+   branch that does not build on any other branch, a branch that is in
+   sync with the branch it builds on, and a branch that is configured
+   to build on some other branch that no longer exists.
+
+ * A packfile that stores the same object more than once is broken and
+   will be rejected by "git index-pack" that is run when receiving
+   data over the wire.
+
+ * Earlier we started rejecting an attempt to add 0{40} object name to
+   the index and to tree objects, but it sometimes is necessary to
+   allow so to be able to use tools like filter-branch to correct such
+   broken tree objects.  "filter-branch" can again be used to to do
+   so.
+
+ * "git config" did not provide a way to set or access numbers larger
+   than a native "int" on the platform; it now provides 64-bit signed
+   integers on all platforms.
+
+ * "git pull --rebase" always chose to do the bog-standard flattening
+   rebase.  You can tell it to run "rebase --preserve-merges" by
+   setting "pull.rebase" configuration to "preserve".
+
+ * "git push --no-thin" actually disables the "thin pack transfer"
+   optimization.
+
  * Magic pathspecs like ":(icase)makefile" that matches both
    Makefile and makefile can be used in more places.
 
@@ -115,6 +179,10 @@
 
 Performance, Internal Implementation, etc.
 
+ * If a build-time fallback is set to "cat" instead of "less", we
+   should apply the same "no subprocess or pipe" optimization as we
+   apply to user-supplied GIT_PAGER=cat.
+
  * Many commands use --dashed-option as a operation mode selector
    (e.g. "git tag --delete") that the user can use at most one
    (e.g. "git tag --delete --verify" is a nonsense) and you cannot
@@ -144,6 +212,87 @@
 track are contained in this release (see release notes to them for
 details).
 
+ * When running "fetch -q", a long silence while the sender side
+   computes the set of objects to send can be mistaken by proxies as
+   dropped connection.  The server side has been taught to send a
+   small empty messages to keep the connection alive.
+   (merge 115dedd jk/upload-pack-keepalive later to maint).
+
+ * "git rebase" had a portability regression in v1.8.4 to trigger a
+   bug in some BSD shell implementations.
+   (merge 99855dd mm/rebase-continue-freebsd-WB later to maint).
+
+ * "git branch --track" had a minor regression in v1.8.3.2 and later
+   that made it impossible to base your local work on anything but a
+   local branch of the upstream repository you are tracking from.
+   (merge b0f49ff jh/checkout-auto-tracking later to maint).
+
+ * When the webserver responds with "405 Method Not Allowed", "git
+   http-backend" should tell the client what methods are allowed with
+   the "Allow" header.
+   (merge 9247be0 bc/http-backend-allow-405 later to maint).
+
+ * When there is no sufficient overlap between old and new history
+   during a "git fetch" into a shallow repository, objects that the
+   sending side knows the receiving end has were unnecessarily sent.
+   (merge f21d2a7 nd/fetch-into-shallow later to maint).
+
+ * "git cvsserver" computed the permission mode bits incorrectly for
+   executable files.
+   (merge 1b48d56 jc/cvsserver-perm-bit-fix later to maint).
+
+ * When send-email comes up with an error message to die with upon
+   failure to start an SSL session, it tried to read the error string
+   from a wrong place.
+   (merge 6cb0c88 bc/send-email-ssl-die-message-fix later to maint).
+
+ * The implementation of "add -i" has a crippling code to work around
+   ActiveState Perl limitation but it by mistake also triggered on Git
+   for Windows where MSYS perl is used.
+   (merge df17e77 js/add-i-mingw later to maint).
+
+ * We made sure that we notice the user-supplied GIT_DIR is actually a
+   gitfile, but did not do the same when the default ".git" is a
+   gitfile.
+   (merge 487a2b7 nd/git-dir-pointing-at-gitfile later to maint).
+
+ * When an object is not found after checking the packfiles and then
+   loose object directory, read_sha1_file() re-checks the packfiles to
+   prevent racing with a concurrent repacker; teach the same logic to
+   has_sha1_file().
+   (merge 45e8a74 jk/has-sha1-file-retry-packed later to maint).
+
+ * "git commit --author=$name", when $name is not in the canonical
+   "A. U. Thor <au.thor@example.xz>" format, looks for a matching name
+   from existing history, but did not consult mailmap to grab the
+   preferred author name.
+   (merge ea16794 ap/commit-author-mailmap later to maint).
+
+ * "git ls-files -k" needs to crawl only the part of the working tree
+   that may overlap the paths in the index to find killed files, but
+   shared code with the logic to find all the untracked files, which
+   made it unnecessarily inefficient.
+   (merge 680be04 jc/ls-files-killed-optim later to maint).
+
+ * The commit object names in the insn sheet that was prepared at the
+   beginning of "rebase -i" session can become ambiguous as the
+   rebasing progresses and the repository gains more commits. Make
+   sure the internal record is kept with full 40-hex object names.
+   (merge 75c6976 es/rebase-i-no-abbrev later to maint).
+
+ * "git rebase --preserve-merges" internally used the merge machinery
+   and as a side effect, left merge summary message in the log, but
+   when rebasing, there should not be a need for merge summary.
+   (merge a9f739c rt/rebase-p-no-merge-summary later to maint).
+
+ * A call to xread() was used without a loop around to cope with short
+   read in the codepath to stream new contents to a pack.
+   (merge e92527c js/xread-in-full later to maint).
+
+ * "git rebase -i" forgot that the comment character can be
+   configurable while reading its insn sheet.
+   (merge 7bca7af es/rebase-i-respect-core-commentchar later to maint).
+
  * The mailmap support code read past the allocated buffer when the
    mailmap file ended with an incomplete line.
    (merge f972a16 jk/mailmap-incomplete-line later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 00bde9d..c3f7002 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -170,8 +170,8 @@
 	pushNeedsForce::
 		Shown when linkgit:git-push[1] rejects an update that
 		tries to overwrite a remote ref that points at an
-		object that is not a committish, or make the remote
-		ref point at an object that is not a committish.
+		object that is not a commit-ish, or make the remote
+		ref point at an object that is not a commit-ish.
 	statusHints::
 		Show directions on how to proceed from the current
 		state in the output of linkgit:git-status[1], in
@@ -553,22 +553,20 @@
 	When not configured the default commit message editor is used instead.
 
 core.pager::
-	The command that Git will use to paginate output.  Can
-	be overridden with the `GIT_PAGER` environment
-	variable.  Note that Git sets the `LESS` environment
-	variable to `FRSX` if it is unset when it runs the
-	pager.  One can change these settings by setting the
-	`LESS` variable to some other value.  Alternately,
-	these settings can be overridden on a project or
-	global basis by setting the `core.pager` option.
-	Setting `core.pager` has no effect on the `LESS`
-	environment variable behaviour above, so if you want
-	to override Git's default settings this way, you need
-	to be explicit.  For example, to disable the S option
-	in a backward compatible manner, set `core.pager`
-	to `less -+S`.  This will be passed to the shell by
-	Git, which will translate the final command to
-	`LESS=FRSX less -+S`.
+	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').
++
+When the `LESS` environment variable is unset, Git sets it to `FRSX`
+(if `LESS` environment variable is set, Git does not change it at
+all).  If you want to selectively override Git's default setting
+for `LESS`, you can set `core.pager` to e.g. `less -+S`.  This will
+be passed to the shell by Git, which will translate the final
+command to `LESS=FRSX less -+S`. The environment tells the command
+to set the `S` option to chop long lines but the command line
+resets it to the default to fold long lines.
 
 core.whitespace::
 	A comma separated list of common whitespace problems to
@@ -766,6 +764,10 @@
 	"git pull" is run. See "pull.rebase" for doing this in a non
 	branch-specific manner.
 +
+	When preserve, also pass `--preserve-merges` along to 'git rebase'
+	so that locally committed merge commits will not be flattened
+	by running 'git pull'.
++
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
 for details).
@@ -787,8 +789,8 @@
 	working repository in gitweb (see linkgit:git-instaweb[1]).
 
 clean.requireForce::
-	A boolean to make git-clean do nothing unless given -f
-	or -n.   Defaults to true.
+	A boolean to make git-clean do nothing unless given -f,
+	-i or -n.   Defaults to true.
 
 color.branch::
 	A boolean to enable/disable color in the output of
@@ -1879,6 +1881,10 @@
 	pull" is run. See "branch.<name>.rebase" for setting this on a
 	per-branch basis.
 +
+	When preserve, also pass `--preserve-merges` along to 'git rebase'
+	so that locally committed merge commits will not be flattened
+	by running 'git pull'.
++
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
 for details).
@@ -2177,6 +2183,13 @@
 	Set to true to enable --branch by default in linkgit:git-status[1].
 	The option --no-branch takes precedence over this variable.
 
+status.displayCommentPrefix::
+	If set to true, linkgit:git-status[1] will insert a comment
+	prefix before each output line (starting with
+	`core.commentChar`, i.e. `#` by default). This was the
+	behavior of linkgit:git-status[1] in Git 1.8.4 and previous.
+	Defaults to false.
+
 status.showUntrackedFiles::
 	By default, linkgit:git-status[1] and linkgit:git-commit[1] show
 	files which are not currently tracked by Git. Directories which
@@ -2275,6 +2288,17 @@
 	of a hidden ref (by default, such a request is rejected).
 	see also `uploadpack.hiderefs`.
 
+uploadpack.keepalive::
+	When `upload-pack` has started `pack-objects`, there may be a
+	quiet period while `pack-objects` prepares the pack. Normally
+	it would output progress information, but if `--quiet` was used
+	for the fetch, `pack-objects` will output nothing at all until
+	the pack data begins. Some clients and networks may consider
+	the server to be hung and give up. Setting this option instructs
+	`upload-pack` to send an empty keepalive packet every
+	`uploadpack.keepalive` seconds. Setting this option to 0
+	disables keepalive packets entirely. The default is 5 seconds.
+
 url.<base>.insteadOf::
 	Any URL that starts with this value will be rewritten to
 	start, instead, with <base>. In cases where some site serves a
diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index e1fba85..2a18c1f 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -304,7 +304,7 @@
   * linkgit:git-shell[1] can be used as a 'restricted login shell'
     for shared central repository users.
 
-link:howto/update-hook-example.txt[update hook howto] has a good
+link:howto/update-hook-example.html[update hook howto] has a good
 example of managing a shared central repository.
 
 
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index b7cb625..311b336 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -48,7 +48,8 @@
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
-branch so that 'git pull' will appropriately merge from
+branch (specifically the `branch.<name>.remote` and `branch.<name>.merge`
+configuration entries) 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, and
@@ -156,7 +157,8 @@
 
 -t::
 --track::
-	When creating a new branch, set up configuration to mark the
+	When creating a new branch, set up `branch.<name>.remote` and
+	`branch.<name>.merge` configuration entries to mark the
 	start-point branch as "upstream" from the new branch. This
 	configuration will tell git to show the relationship between the
 	two branches in `git status` and `git branch -v`. Furthermore,
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 21cffe2..322f5ed 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -54,7 +54,7 @@
 
 --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
+	<object> has be of the form <tree-ish>:<path>, or :<path> in order
 	to apply the filter to the content recorded in the index at <path>.
 
 --batch::
diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt
index d2df487..ee2e091 100644
--- a/Documentation/git-check-ignore.txt
+++ b/Documentation/git-check-ignore.txt
@@ -45,6 +45,13 @@
 	not be possible to distinguish between paths which match a
 	pattern and those which don't.
 
+--no-index::
+	Don't look in the index when undertaking the checks. This can
+	be used to debug why a path became tracked by e.g. `git add .`
+	and was not ignored by the rules as expected by the user or when
+	developing patterns including negation to match a path previously
+	added with `git add -f`.
+
 OUTPUT
 ------
 
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index a49be1b..fc02959 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -54,6 +54,8 @@
 
 . They cannot contain a sequence `@{`.
 
+. They cannot be the single character `@`.
+
 . They cannot contain a `\`.
 
 These rules make it easy for shell script based tools to parse
diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt
index f6c19c7..2d0daae 100644
--- a/Documentation/git-cherry.txt
+++ b/Documentation/git-cherry.txt
@@ -14,8 +14,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 diffs are compared after removing any whitespace and line numbers.
 
 Every commit that doesn't exist in the <upstream> branch
 has its id (sha1) reported, prefixed by a symbol.  The ones that have
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 7da0f13..b211440 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -20,7 +20,7 @@
 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
+link:technical/api-credentials.html[the Git credential API] for more
 background on the concepts.
 
 git-credential takes an "action" option on the command-line (one of
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 9439cd6..d20ca40 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>...
+'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] <commit-ish>...
 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
 
 DESCRIPTION
@@ -26,8 +26,8 @@
 
 OPTIONS
 -------
-<committish>...::
-	Committish object names to describe.
+<commit-ish>...::
+	Commit-ish object names to describe.
 
 --dirty[=<mark>]::
 	Describe the working tree.
@@ -57,7 +57,7 @@
 
 --candidates=<n>::
 	Instead of considering only the 10 most recent tags as
-	candidates to describe the input committish consider
+	candidates to describe the input commit-ish consider
 	up to <n> candidates.  Increasing <n> above 10 will take
 	slightly longer but may produce a more accurate result.
 	An <n> of 0 will cause only exact matches to be output.
@@ -145,7 +145,7 @@
 SEARCH STRATEGY
 ---------------
 
-For each committish supplied, 'git describe' will first look for
+For each commit-ish 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
@@ -154,12 +154,12 @@
 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 SHA-1. If '--first-parent' was
+abbreviation of the input commit-ish's SHA-1. If '--first-parent' was
 specified then the walk will only consider the first parent of each
 commit.
 
 If multiple tags were found during the walk then the tag which
-has the fewest commits different from the input committish will be
+has the fewest commits different from the input commit-ish will be
 selected and output.  Here fewest commits different is defined as
 the number of commits which would be shown by `git log tag..input`
 will be the smallest number of commits possible.
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 78d6d50..33fbd8c 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -28,10 +28,15 @@
 	words, the differences are what you _could_ tell Git to
 	further add to the index but you still haven't.  You can
 	stage these changes by using linkgit:git-add[1].
-+
-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' --no-index [--options] [--] [<path>...]::
+
+	This form is to compare the given two paths on the
+	filesystem.  You can omit the `--no-index` option when
+	running the command in a working tree controlled by Git and
+	at least one of the paths points outside the working tree,
+	or when running the command outside a working tree
+	controlled by Git.
 
 'git diff' [--options] --cached [<commit>] [--] [<path>...]::
 
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index bf1a02a..73f9806 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -361,8 +361,8 @@
 	`--cat-blob-fd` or `stdout` if unspecified.
 
 `feature`::
-	Require that fast-import supports the specified feature, or
-	abort if it does not.
+	Enable the specified feature. This requires that fast-import
+	supports the specified feature, and aborts if it does not.
 
 `option`::
 	Specify any of the options listed under OPTIONS that do not
@@ -380,8 +380,8 @@
 	('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)?
+	('from' SP <commit-ish> LF)?
+	('merge' SP <commit-ish> LF)?
 	(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
 	LF?
 ....
@@ -460,9 +460,9 @@
 be the first ancestor of the new commit.
 
 As `LF` is not valid in a Git refname or SHA-1 expression, no
-quoting or escaping syntax is supported within `<committish>`.
+quoting or escaping syntax is supported within `<commit-ish>`.
 
-Here `<committish>` is any of the following:
+Here `<commit-ish>` 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, it's treated as a SHA-1
@@ -509,7 +509,7 @@
 it is suggested that frontends do not use more than 15 `merge`
 commands per commit; 16, if starting a new, empty branch.
 
-Here `<committish>` is any of the commit specification expressions
+Here `<commit-ish>` is any of the commit specification expressions
 also accepted by `from` (see above).
 
 `filemodify`
@@ -677,8 +677,8 @@
 `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>`
+annotating a `<commit-ish>` or change this annotation contents.
+Internally it is similar to filemodify 100644 on `<commit-ish>`
 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.
@@ -691,7 +691,7 @@
 	commit that is to be annotated.
 +
 ....
-	'N' SP <dataref> SP <committish> LF
+	'N' SP <dataref> SP <commit-ish> LF
 ....
 +
 Here `<dataref>` can be either a mark reference (`:<idnum>`)
@@ -704,13 +704,13 @@
 	command.
 +
 ....
-	'N' SP 'inline' SP <committish> LF
+	'N' SP 'inline' SP <commit-ish> LF
 	data
 ....
 +
 See below for a detailed description of the `data` command.
 
-In both formats `<committish>` is any of the commit specification
+In both formats `<commit-ish>` is any of the commit specification
 expressions also accepted by `from` (see above).
 
 `mark`
@@ -741,7 +741,7 @@
 
 ....
 	'tag' SP <name> LF
-	'from' SP <committish> LF
+	'from' SP <commit-ish> LF
 	'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
 	data
 ....
@@ -786,11 +786,11 @@
 
 ....
 	'reset' SP <ref> LF
-	('from' SP <committish> LF)?
+	('from' SP <commit-ish> LF)?
 	LF?
 ....
 
-For a detailed description of `<ref>` and `<committish>` see above
+For a detailed description of `<ref>` and `<commit-ish>` see above
 under `commit` and `from`.
 
 The `LF` after the command is optional (it used to be required).
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index c5f84b6..58731c1 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -13,7 +13,7 @@
 
 DESCRIPTION
 -----------
-Reads three treeish, and output trivial merge results and
+Reads three tree-ish, and output trivial merge results and
 conflicting stages to the standard output.  This is similar to
 what three-way 'git read-tree -m' does, but instead of storing the
 results in the index, the command outputs the entries to the
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index 15b00e0..ca28fb8 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git name-rev' [--tags] [--refs=<pattern>]
-	       ( --all | --stdin | <committish>... )
+	       ( --all | --stdin | <commit-ish>... )
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 6ef8d59..beea10b 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -102,12 +102,18 @@
 :git-pull: 1
 
 -r::
---rebase::
-	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.
+--rebase[=false|true|preserve]::
+	When 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.
++
+When preserve, also rebase the current branch on top of the upstream
+branch, but pass `--preserve-merges` along to `git rebase` so that
+locally created merge commits will not be flattened.
++
+When false, merge the current branch into the upstream branch.
 +
 See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
 linkgit:git-config[1] if you want to make `git pull` always use
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index e2992f1..9eec740 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -121,7 +121,7 @@
 --follow-tags::
 	Push all the refs that would be pushed without this option,
 	and also push annotated tags in `refs/tags` that are missing
-	from the remote but are pointing at committish that are
+	from the remote but are pointing at commit-ish that are
 	reachable from the refs being pushed.
 
 --receive-pack=<git-receive-pack>::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 6b2e1c8..94e07fd 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -322,7 +322,7 @@
 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).
+link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
 
 --ignore-whitespace::
 --whitespace=<option>::
@@ -416,7 +416,7 @@
 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).
+link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
 
 include::merge-strategies.txt[]
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index f79c9d8..2de67a5 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -59,7 +59,7 @@
 changes introduced by commits that are not ancestors of the previously
 reverted merge.  This may or may not be what you want.
 +
-See the link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for
+See the link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for
 more details.
 
 --no-edit::
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 0df13ff..0a0a551 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>])
+'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
 
 DESCRIPTION
 -----------
@@ -58,6 +58,58 @@
 With `-d` flag, it deletes the named <ref> after verifying it
 still contains <oldvalue>.
 
+With `--stdin`, update-ref reads instructions from standard input and
+performs all modifications together.  Specify commands of the form:
+
+	update SP <ref> SP <newvalue> [SP <oldvalue>] LF
+	create SP <ref> SP <newvalue> LF
+	delete SP <ref> [SP <oldvalue>] LF
+	verify SP <ref> [SP <oldvalue>] LF
+	option SP <opt> LF
+
+Quote fields containing whitespace as if they were strings in C source
+code.  Alternatively, use `-z` to specify commands without quoting:
+
+	update SP <ref> NUL <newvalue> NUL [<oldvalue>] NUL
+	create SP <ref> NUL <newvalue> NUL
+	delete SP <ref> NUL [<oldvalue>] NUL
+	verify SP <ref> NUL [<oldvalue>] NUL
+	option SP <opt> NUL
+
+Lines of any other format or a repeated <ref> produce an error.
+Command meanings are:
+
+update::
+	Set <ref> to <newvalue> after verifying <oldvalue>, if given.
+	Specify a zero <newvalue> to ensure the ref does not exist
+	after the update and/or a zero <oldvalue> to make sure the
+	ref does not exist before the update.
+
+create::
+	Create <ref> with <newvalue> after verifying it does not
+	exist.  The given <newvalue> may not be zero.
+
+delete::
+	Delete <ref> after verifying it exists with <oldvalue>, if
+	given.  If given, <oldvalue> may not be zero.
+
+verify::
+	Verify <ref> against <oldvalue> but do not change it.  If
+	<oldvalue> zero or missing, the ref must not exist.
+
+option::
+	Modify behavior of the next command naming a <ref>.
+	The only valid option is `no-deref` to avoid dereferencing
+	a symbolic ref.
+
+Use 40 "0" or the empty string to specify a zero value, except that
+with `-z` an empty <oldvalue> is considered missing.
+
+If all <ref>s can be locked with matching <oldvalue>s
+simultaneously, all modifications are performed.  Otherwise, no
+modifications are performed.  Note that while each individual
+<ref> is updated or deleted atomically, a concurrent reader may
+still see a subset of the modifications.
 
 Logging Updates
 ---------------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index c4f0ed5..5d68d33 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -9,7 +9,7 @@
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--help] [-c <name>=<value>]
+'git' [--version] [--help] [-C <path>] [-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>]
@@ -395,6 +395,20 @@
 because `git --help ...` is converted internally into `git
 help ...`.
 
+-C <path>::
+	Run as if git was started in '<path>' instead of the current working
+	directory.  When multiple `-C` options are given, each subsequent
+	non-absolute `-C <path>` is interpreted relative to the preceding `-C
+	<path>`.
++
+This option affects options that expect path name like `--git-dir` and
+`--work-tree` in that their interpretations of the path names would be
+made relative to the working directory caused by the `-C` option. For
+example the following invocations are equivalent:
+
+    git --git-dir=a.git --work-tree=b -C c status
+    git --git-dir=c/a.git --work-tree=c/b status
+
 -c <name>=<value>::
 	Pass a configuration parameter to the command. The value
 	given will override values from configuration files.
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 9ac5088..7d54b77 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -106,7 +106,7 @@
 +
 ---------------------------------------------
 $ git describe -h
-usage: git describe [options] <committish>*
+usage: git describe [options] <commit-ish>*
    or: git describe [options] --dirty
 
     --contains            find the tag that comes after the commit
diff --git a/Documentation/gitcvs-migration.txt b/Documentation/gitcvs-migration.txt
index 5ab5b07..5ea94cb 100644
--- a/Documentation/gitcvs-migration.txt
+++ b/Documentation/gitcvs-migration.txt
@@ -157,7 +157,7 @@
 repository to a mailing list.  See linkgit:githooks[5].
 
 You can enforce finer grained permissions using update hooks.  See
-link:howto/update-hook-example.txt[Controlling access to branches using
+link:howto/update-hook-example.html[Controlling access to branches using
 update hooks].
 
 Providing CVS Access to a Git Repository
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index bc069c2..f1f4ca9 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -120,6 +120,11 @@
 When choosing between 'push' and 'export', Git prefers 'push'.
 Other frontends may have some other order of preference.
 
+'no-private-update'::
+	When using the 'refspec' capability, git normally updates the
+	private ref on successful push. This update is disabled when
+	the remote-helper declares the capability 'no-private-update'.
+
 
 Capabilities for Fetching
 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -180,6 +185,12 @@
 advertised with this capability must cover all refs reported by
 the list command.  If no 'refspec' capability is advertised,
 there is an implied `refspec *:*`.
++
+When writing remote-helpers for decentralized version control
+systems, it is advised to keep a local copy of the repository to
+interact with, and to let the private namespace refs point to this
+local repository, while the refs/remotes namespace is used to track
+the remote repository.
 
 'bidi-import'::
 	This modifies the 'import' capability.
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index 13a64d3..e470661 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -82,6 +82,18 @@
 	to the top <<def_directory,directory>> of the stored
 	revision.
 
+[[def_commit-ish]]commit-ish (also committish)::
+	A <<def_commit_object,commit object>> or an
+	<<def_object,object>> that can be recursively dereferenced to
+	a commit object.
+	The following are all commit-ishes:
+	a commit object,
+	a <<def_tag_object,tag object>> that points to a commit
+	object,
+	a tag object that points to a tag object that points to a
+	commit object,
+	etc.
+
 [[def_core_git]]core Git::
 	Fundamental data structures and utilities of Git. Exposes only limited
 	source code management tools.
@@ -427,10 +439,20 @@
 	to the result.
 
 [[def_ref]]ref::
-	A 40-byte hex representation of a <<def_SHA1,SHA-1>> or a name that
-	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.
+	A name that begins with `refs/` (e.g. `refs/heads/master`)
+	that points to an <<def_object_name,object name>> or another
+	ref (the latter is called a <<def_symref,symbolic ref>>).
+	For convenience, a ref can sometimes be abbreviated when used
+	as an argument to a Git command; see linkgit:gitrevisions[7]
+	for details.
+	Refs are stored in the <<def_repository,repository>>.
++
+The ref namespace is hierarchical.
+Different subhierarchies are used for different purposes (e.g. the
+`refs/heads/` hierarchy is used to represent local branches).
++
+There are a few special-purpose refs that do not begin with `refs/`.
+The most notable example is `HEAD`.
 
 [[def_reflog]]reflog::
 	A reflog shows the local "history" of a ref.  In other words,
@@ -530,10 +552,19 @@
 	with refs to the associated blob and/or tree objects. A
 	<<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
 
-[[def_tree-ish]]tree-ish::
-	A <<def_ref,ref>> pointing to either a <<def_commit_object,commit
-	object>>, a <<def_tree_object,tree object>>, or a <<def_tag_object,tag
-	object>> pointing to a tag or commit or tree object.
+[[def_tree-ish]]tree-ish (also treeish)::
+	A <<def_tree_object,tree object>> or an <<def_object,object>>
+	that can be recursively dereferenced to a tree object.
+	Dereferencing a <<def_commit_object,commit object>> yields the
+	tree object corresponding to the <<def_revision,revision>>'s
+	top <<def_directory,directory>>.
+	The following are all tree-ishes:
+	a <<def_commit-ish,commit-ish>>,
+	a tree object,
+	a <<def_tag_object,tag object>> that points to a tree object,
+	a tag object that points to a tag object that points to a tree
+	object,
+	etc.
 
 [[def_unmerged_index]]unmerged index::
 	An <<def_index,index>> which contains unmerged
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index 0d5419e..85f69db 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -154,7 +154,7 @@
 Packing 0 objects
 Unpacking 0 objects
 
-* committish: e3a693c...	refs/heads/master from .
+* commit-ish: e3a693c...	refs/heads/master from .
 Trying to merge e3a693c... into 8c1f5f0... using 10d781b...
 Committed merge 7fb9b7262a1d1e0a47bbfdcbbcf50ce0635d3f8f
  cache.h        |    8 ++++----
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index d477b3f..2c06ed3 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -58,6 +58,9 @@
 While the ref name encoding is unspecified, UTF-8 is preferred as
 some output processing may assume ref names in UTF-8.
 
+'@'::
+  '@' alone is a shortcut for 'HEAD'.
+
 '<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
   A ref followed by the suffix '@' with a date specification
   enclosed in a brace
@@ -111,16 +114,23 @@
 
 '<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'
+  brace pair means dereference the object at '<rev>' recursively until
+  an object of type '<type>' is found or the object cannot be
+  dereferenced anymore (in which case, barf).
+  For example, if '<rev>' is a commit-ish, '<rev>{caret}\{commit\}'
+  describes the corresponding commit object.
+  Similarly, if '<rev>' is a tree-ish, '<rev>{caret}\{tree\}'
+  describes the corresponding tree object.
+  '<rev>{caret}0'
   is a short-hand for '<rev>{caret}\{commit\}'.
 +
 'rev{caret}\{object\}' can be used to make sure 'rev' names an
 object that exists, without requiring 'rev' to be a tag, and
 without dereferencing 'rev'; because a tag is already an object,
 it does not have to be dereferenced even once to get to an object.
++
+'rev{caret}\{tag\}' can be used to ensure that 'rev' identifies an
+existing tag object.
 
 '<rev>{caret}\{\}', e.g. 'v0.99.8{caret}\{\}'::
   A suffix '{caret}' followed by an empty brace pair
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index a1173ee..caf941a 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -499,5 +499,5 @@
 
 link:http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
 link:http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
-link:technical/pack-protocol.txt
-link:technical/protocol-capabilities.txt
+link:technical/pack-protocol.html
+link:technical/protocol-capabilities.html
diff --git a/Documentation/technical/pack-heuristics.txt b/Documentation/technical/pack-heuristics.txt
index 8b7ae1c..b7bd951 100644
--- a/Documentation/technical/pack-heuristics.txt
+++ b/Documentation/technical/pack-heuristics.txt
@@ -366,12 +366,6 @@
 
     <linus> Yes, we always write out most recent first
 
-For the other record:
-
-    <pasky> njs`: http://pastebin.com/547965
-
-The 'net never forgets, so that should be good until the end of time.
-
     <njs`> And, yeah, I got the part about deeper-in-history stuff
         having worse IO characteristics, one sort of doesn't care.
 
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index fe723e4..cbb01a1 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1,6 +1,5 @@
-Git User's Manual (for version 1.5.3 or newer)
-______________________________________________
-
+Git User Manual
+_______________
 
 Git is a fast distributed revision control system.
 
@@ -220,7 +219,7 @@
 
 The best way to see how this works is using the linkgit:gitk[1]
 command; running gitk now on a Git repository and looking for merge
-commits will help understand how the Git organizes history.
+commits will help understand how Git organizes history.
 
 In the following, we say that commit X is "reachable" from commit Y
 if commit X is an ancestor of commit Y.  Equivalently, you could say
@@ -269,27 +268,23 @@
 a summary of the commands:
 
 `git branch`::
-	list all branches
+	list all branches.
 `git branch <branch>`::
 	create a new branch named `<branch>`, referencing the same
-	point in history as the current branch
+	point in history as the current branch.
 `git branch <branch> <start-point>`::
 	create a new branch named `<branch>`, referencing
 	`<start-point>`, which may be specified any way you like,
-	including using a branch name or a tag name
+	including using a branch name or a tag name.
 `git branch -d <branch>`::
-	delete the branch `<branch>`; if the branch you are deleting
-	points to a commit which is not reachable from the current
-	branch, this command will fail with a warning.
+	delete the branch `<branch>`; if the branch is not fully
+	merged in its upstream branch or contained in the current branch,
+	this command will fail with a warning.
 `git branch -D <branch>`::
-	even if the branch points to a commit not reachable
-	from the current branch, you may know that that commit
-	is still reachable from some other branch or tag.  In that
-	case it is safe to use this command to force Git to delete
-	the branch.
+	delete the branch `<branch>` irrespective of its merged status.
 `git checkout <branch>`::
 	make the current branch `<branch>`, updating the working
-	directory to reflect the version referenced by `<branch>`
+	directory to reflect the version referenced by `<branch>`.
 `git checkout -b <new> <start-point>`::
 	create a new branch `<new>` referencing `<start-point>`, and
 	check it out.
@@ -313,10 +308,17 @@
 
 ------------------------------------------------
 $ git checkout v2.6.17
-Note: moving to "v2.6.17" 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>
+Note: checking out 'v2.6.17'.
+
+You are in 'detached HEAD' state. You can look around, make experimental
+changes and commit them, and you can discard any commits you make in this
+state without impacting any branches by performing another checkout.
+
+If you want to create a new branch to retain commits you create, 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 427abfa... Linux v2.6.17
 ------------------------------------------------
 
@@ -327,7 +329,7 @@
 $ cat .git/HEAD
 427abfa28afedffadfca9dd8b067eb6d36bac53f
 $ git branch
-* (no branch)
+* (detached from v2.6.17)
   master
 ------------------------------------------------
 
@@ -787,7 +789,7 @@
 -------------------------------------------------
 
 Or you could recall that the `...` operator selects all commits
-contained reachable from either one reference or the other but not
+reachable from either one reference or the other but not
 both; so
 
 -------------------------------------------------
@@ -814,7 +816,7 @@
 $ gitk e05db0fd..
 -------------------------------------------------
 
-Or you can use linkgit:git-name-rev[1], which will give the commit a
+or you can use linkgit:git-name-rev[1], which will give the commit a
 name based on any tag it finds pointing to one of the commit's
 descendants:
 
@@ -858,8 +860,8 @@
 
 As yet another alternative, the linkgit:git-show-branch[1] command lists
 the commits reachable from its arguments with a display on the left-hand
-side that indicates which arguments that commit is reachable from.  So,
-you can run something like
+side that indicates which arguments that commit is reachable from.
+So, if you run something like
 
 -------------------------------------------------
 $ git show-branch e05db0fd v1.5.0-rc0 v1.5.0-rc1 v1.5.0-rc2
@@ -871,15 +873,15 @@
 ...
 -------------------------------------------------
 
-then search for a line that looks like
+then a line like
 
 -------------------------------------------------
 + ++ [e05db0fd] Fix warnings in sha1_file.c - use C99 printf format if
 available
 -------------------------------------------------
 
-Which shows that e05db0fd is reachable from itself, from v1.5.0-rc1, and
-from v1.5.0-rc2, but not from v1.5.0-rc0.
+shows that e05db0fd is reachable from itself, from v1.5.0-rc1,
+and from v1.5.0-rc2, and not from v1.5.0-rc0.
 
 [[showing-commits-unique-to-a-branch]]
 Showing commits unique to a given branch
@@ -1074,19 +1076,13 @@
 
 Modifying the index is easy:
 
-To update the index with the new contents of a modified file, use
+To update the index with the contents of a new or modified file, use
 
 -------------------------------------------------
 $ git add path/to/file
 -------------------------------------------------
 
-To add the contents of a new file to the index, use
-
--------------------------------------------------
-$ git add path/to/file
--------------------------------------------------
-
-To remove a file from the index and from the working tree,
+To remove a file from the index and from the working tree, use
 
 -------------------------------------------------
 $ git rm path/to/file
@@ -1787,7 +1783,7 @@
 $ git merge branch
 -------------------------------------------------
 
-are roughly equivalent.  The former is actually very commonly used.
+are roughly equivalent.
 
 [[submitting-patches]]
 Submitting patches to a project
@@ -1977,7 +1973,7 @@
 -------------------------------------------------
 
 (See also
-link:howto/setup-git-server-over-http.txt[setup-git-server-over-http]
+link:howto/setup-git-server-over-http.html[setup-git-server-over-http]
 for a slightly more sophisticated setup using WebDAV which also
 allows pushing over HTTP.)
 
@@ -2249,11 +2245,11 @@
 $ ... patch ... test  ... commit [ ... patch ... test ... commit ]*
 -------------------------------------------------
 
-When you are happy with the state of this change, you can pull it into the
+When you are happy with the state of this change, you can merge it into the
 "test" branch in preparation to make it public:
 
 -------------------------------------------------
-$ git checkout test && git pull . speed-up-spinlocks
+$ git checkout test && git merge speed-up-spinlocks
 -------------------------------------------------
 
 It is unlikely that you would have any conflicts here ... but you might if you
@@ -2265,7 +2261,7 @@
 means that the patches can be moved into the `release` tree in any order.
 
 -------------------------------------------------
-$ git checkout release && git pull . speed-up-spinlocks
+$ git checkout release && git merge speed-up-spinlocks
 -------------------------------------------------
 
 After a while, you will have a number of branches, and despite the
@@ -3191,23 +3187,21 @@
 You can save space and make Git faster by moving these loose objects in
 to a "pack file", which stores a group of objects in an efficient
 compressed format; the details of how pack files are formatted can be
-found in link:technical/pack-format.txt[technical/pack-format.txt].
+found in link:technical/pack-format.html[pack format].
 
 To put the loose objects into a pack, just run git repack:
 
 ------------------------------------------------
 $ git repack
-Generating pack...
-Done counting 6020 objects.
-Deltifying 6020 objects.
- 100% (6020/6020) done
-Writing 6020 objects.
- 100% (6020/6020) done
-Total 6020, written 6020 (delta 4070), reused 0 (delta 0)
-Pack pack-3e54ad29d5b2e05838c75df582c65257b8d08e1c created.
+Counting objects: 6020, done.
+Delta compression using up to 4 threads.
+Compressing objects: 100% (6020/6020), done.
+Writing objects: 100% (6020/6020), done.
+Total 6020 (delta 4070), reused 0 (delta 0)
 ------------------------------------------------
 
-You can then run
+This creates a single "pack file" in .git/objects/pack/
+containing all currently unpacked objects.  You can then run
 
 ------------------------------------------------
 $ git prune
@@ -3305,17 +3299,11 @@
 $ git prune
 ------------------------------------------------
 
-and they'll be gone. But you should only run `git prune` on a quiescent
+and they'll be gone. (You should only run `git prune` on a quiescent
 repository--it's kind of like doing a filesystem fsck recovery: you
 don't want to do that while the filesystem is mounted.
-
-(The same is true of `git fsck` itself, btw, but since
-`git fsck` never actually *changes* the repository, it just reports
-on what it found, `git fsck` itself is never 'dangerous' to run.
-Running it while somebody is actually changing the repository can cause
-confusing and scary messages, but it won't actually do anything bad. In
-contrast, running `git prune` while somebody is actively changing the
-repository is a *BAD* idea).
+`git prune` is designed not to cause any harm in such cases of concurrent
+accesses to a repository but you might receive confusing or scary messages.)
 
 [[recovering-from-repository-corruption]]
 Recovering from repository corruption
@@ -3538,7 +3526,7 @@
 manually check them out; earlier versions won't recognize the submodules at
 all.
 
-To see how submodule support works, create (for example) four example
+To see how submodule support works, create four example
 repositories that can be used later as a submodule:
 
 -------------------------------------------------
@@ -3640,7 +3628,7 @@
 
 -------------------------------------------------
 $ git branch
-* (no branch)
+* (detached from d266b98)
   master
 -------------------------------------------------
 
@@ -3910,7 +3898,7 @@
 previous states represented by other commits.
 
 In other words, while a "tree" represents a particular directory state
-of a working directory, a "commit" represents that state in "time",
+of a working directory, a "commit" represents that state in time,
 and explains how we got there.
 
 You create a commit object by giving it the tree that describes the
@@ -3930,8 +3918,7 @@
 result to the file pointed at by `.git/HEAD`, so that we can always see
 what the last committed state was.
 
-Here is an ASCII art by Jon Loeliger that illustrates how
-various pieces fit together.
+Here is a picture that illustrates how various pieces fit together:
 
 ------------
 
@@ -4010,27 +3997,26 @@
 Merging multiple trees
 ----------------------
 
-Git helps you do a three-way merge, which you can expand to n-way by
-repeating the merge procedure arbitrary times until you finally
-"commit" the state.  The normal situation is that you'd only do one
-three-way merge (two parents), and commit it, but if you like to, you
-can do multiple parents in one go.
+Git can help you perform a three-way merge, which can in turn be
+used for a many-way merge by repeating the merge procedure several
+times.  The usual situation is that you only do one three-way merge
+(reconciling two lines of history) and commit the result, but if
+you like to, you can merge several branches in one go.
 
-To do a three-way merge, you need the two sets of "commit" objects
-that you want to merge, use those to find the closest common parent (a
-third "commit" object), and then use those commit objects to find the
-state of the directory ("tree" object) at these points.
+To perform a three-way merge, you start with the two commits you
+want to merge, find their closest common parent (a third commit),
+and compare the trees corresponding to these three commits.
 
-To get the "base" for the merge, you first look up the common parent
-of two commits with
+To get the "base" for the merge, look up the common parent of two
+commits:
 
 -------------------------------------------------
 $ git merge-base <commit1> <commit2>
 -------------------------------------------------
 
-which will return you the commit they are both based on.  You should
-now look up the "tree" objects of those commits, which you can easily
-do with (for example)
+This prints the name of a commit they are both based on. You should
+now look up the tree objects of those commits, which you can easily
+do with
 
 -------------------------------------------------
 $ git cat-file commit <commitname> | head -1
@@ -4152,8 +4138,6 @@
 that is used to name the object is the hash of the original data
 plus this header, so `sha1sum` 'file' does not match the object name
 for 'file'.
-(Historical note: in the dawn of the age of Git the hash
-was the SHA-1 of the 'compressed' object.)
 
 As a result, the general consistency of an object can always be tested
 independently of the contents or the type of the object: all objects can
diff --git a/Makefile b/Makefile
index e2abb7b..de3d72c 100644
--- a/Makefile
+++ b/Makefile
@@ -485,11 +485,9 @@
 SCRIPT_PERL += git-send-email.perl
 SCRIPT_PERL += git-svn.perl
 
-SCRIPT_PYTHON += git-remote-testpy.py
 SCRIPT_PYTHON += git-p4.py
 
 NO_INSTALL += git-remote-testgit
-NO_INSTALL += git-remote-testpy
 
 # Generated files for scripts
 SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
@@ -1665,9 +1663,6 @@
 ifndef NO_PERL
 	$(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
-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:
@@ -1835,12 +1830,7 @@
 $(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 $(SCRIPT_PYTHON_GEN): % : %.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' \
 	    $< >$@+ && \
 	chmod +x $@+ && \
 	mv $@+ $@
@@ -2347,9 +2337,6 @@
 	$(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
 	$(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
@@ -2497,9 +2484,6 @@
 	$(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
diff --git a/bisect.c b/bisect.c
index 71c1958..1e46a4f 100644
--- a/bisect.c
+++ b/bisect.c
@@ -624,7 +624,7 @@
 	if (prepare_revision_walk(revs))
 		die("revision walk setup failed");
 	if (revs->tree_objects)
-		mark_edges_uninteresting(revs->commits, revs, NULL);
+		mark_edges_uninteresting(revs, NULL);
 }
 
 static void exit_if_skipped_commits(struct commit_list *tried,
diff --git a/branch.c b/branch.c
index c5c6984..9e6c68e 100644
--- a/branch.c
+++ b/branch.c
@@ -203,8 +203,7 @@
 	struct refspec query;
 	memset(&query, 0, sizeof(struct refspec));
 	query.dst = tracking_branch;
-	return !(remote_find_tracking(remote, &query) ||
-		 prefixcmp(query.src, "refs/heads/"));
+	return !remote_find_tracking(remote, &query);
 }
 
 static int validate_remote_tracking_branch(char *ref)
@@ -291,7 +290,7 @@
 	hashcpy(sha1, commit->object.sha1);
 
 	if (!dont_change_ref) {
-		lock = lock_any_ref_for_update(ref.buf, NULL, 0);
+		lock = lock_any_ref_for_update(ref.buf, NULL, 0, NULL);
 		if (!lock)
 			die_errno(_("Failed to lock ref for update"));
 	}
@@ -307,7 +306,7 @@
 			 start_name);
 
 	if (real_ref && track)
-		setup_tracking(ref.buf+11, real_ref, track, quiet);
+		setup_tracking(ref.buf + 11, real_ref, track, quiet);
 
 	if (!dont_change_ref)
 		if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/builtin/add.c b/builtin/add.c
index 31ddabd..226f758 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -296,7 +296,7 @@
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 
 	if (read_cache() < 0)
-		die (_("Could not read the index"));
+		die(_("Could not read the index"));
 
 	init_revisions(&rev, prefix);
 	rev.diffopt.context = 7;
@@ -307,11 +307,11 @@
 	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);
+		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"));
+		die(_("Could not write patch"));
 
 	launch_editor(file, NULL, NULL);
 
@@ -324,7 +324,7 @@
 	child.git_cmd = 1;
 	child.argv = apply_argv;
 	if (run_command(&child))
-		die (_("Could not apply '%s'"), file);
+		die(_("Could not apply '%s'"), file);
 
 	unlink(file);
 	free(file);
@@ -582,7 +582,7 @@
 
 	unplug_bulk_checkin();
 
- finish:
+finish:
 	if (active_cache_changed) {
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(&lock_file))
diff --git a/builtin/branch.c b/builtin/branch.c
index 0903763..ad0f86d 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -423,19 +423,19 @@
 	char *ref = NULL;
 	struct branch *branch = branch_get(branch_name);
 	struct strbuf fancy = STRBUF_INIT;
+	int upstream_is_gone = 0;
 
-	if (!stat_tracking_info(branch, &ours, &theirs)) {
-		if (branch && branch->merge && branch->merge[0]->dst &&
-		    show_upstream_ref) {
-			ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
-			if (want_color(branch_use_color))
-				strbuf_addf(stat, "[%s%s%s] ",
-						branch_get_color(BRANCH_COLOR_UPSTREAM),
-						ref, branch_get_color(BRANCH_COLOR_RESET));
-			else
-				strbuf_addf(stat, "[%s] ", ref);
-		}
+	switch (stat_tracking_info(branch, &ours, &theirs)) {
+	case 0:
+		/* no base */
 		return;
+	case -1:
+		/* with "gone" base */
+		upstream_is_gone = 1;
+		break;
+	default:
+		/* with base */
+		break;
 	}
 
 	if (show_upstream_ref) {
@@ -448,19 +448,25 @@
 			strbuf_addstr(&fancy, ref);
 	}
 
-	if (!ours) {
-		if (ref)
+	if (upstream_is_gone) {
+		if (show_upstream_ref)
+			strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
+	} else if (!ours && !theirs) {
+		if (show_upstream_ref)
+			strbuf_addf(stat, _("[%s]"), fancy.buf);
+	} else if (!ours) {
+		if (show_upstream_ref)
 			strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
 		else
 			strbuf_addf(stat, _("[behind %d]"), theirs);
 
 	} else if (!theirs) {
-		if (ref)
+		if (show_upstream_ref)
 			strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
 		else
 			strbuf_addf(stat, _("[ahead %d]"), ours);
 	} else {
-		if (ref)
+		if (show_upstream_ref)
 			strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
 				    fancy.buf, ours, theirs);
 		else
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index e2a1cef..594463a 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -5,7 +5,7 @@
 #include "pathspec.h"
 #include "parse-options.h"
 
-static int quiet, verbose, stdin_paths, show_non_matching;
+static int quiet, verbose, stdin_paths, show_non_matching, no_index;
 static const char * const check_ignore_usage[] = {
 "git check-ignore [options] pathname...",
 "git check-ignore [options] --stdin < <list-of-paths>",
@@ -24,6 +24,8 @@
 		 N_("terminate input and output records by a NUL character")),
 	OPT_BOOL('n', "non-matching", &show_non_matching,
 		 N_("show non-matching input paths")),
+	OPT_BOOL(0, "no-index", &no_index,
+		 N_("ignore index when checking")),
 	OPT_END()
 };
 
@@ -166,7 +168,7 @@
 		die(_("--non-matching is only valid with --verbose"));
 
 	/* read_cache() is only necessary so we can watch out for submodules. */
-	if (read_cache() < 0)
+	if (!no_index && read_cache() < 0)
 		die(_("index file corrupt"));
 
 	memset(&dir, 0, sizeof(dir));
diff --git a/builtin/clone.c b/builtin/clone.c
index ca3eb68..0aff974 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -556,7 +556,7 @@
 							      0, &rm, transport))
 			die(_("remote did not send all necessary objects"));
 		if (0 <= option_verbosity)
-			printf(_("done\n"));
+			printf(_("done.\n"));
 	}
 
 	if (refs) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 80d886a..6ab4605 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -30,6 +30,7 @@
 #include "column.h"
 #include "sequencer.h"
 #include "notes-utils.h"
+#include "mailmap.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [options] [--] <pathspec>..."),
@@ -163,6 +164,15 @@
 		s->whence = whence;
 }
 
+static void status_init_config(struct wt_status *s, config_fn_t fn)
+{
+	wt_status_prepare(s);
+	gitmodules_config();
+	git_config(fn, s);
+	determine_whence(s);
+	s->hints = advice_status_hints; /* must come after git_config() */
+}
+
 static void rollback_index_files(void)
 {
 	switch (commit_style) {
@@ -597,6 +607,7 @@
 	const char *hook_arg2 = NULL;
 	int ident_shown = 0;
 	int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+	int old_display_comment_prefix;
 
 	/* This checks and barfs if author is badly specified */
 	determine_author_info(author_ident);
@@ -694,6 +705,16 @@
 	if (s->fp == NULL)
 		die_errno(_("could not open '%s'"), git_path(commit_editmsg));
 
+	/* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */
+	old_display_comment_prefix = s->display_comment_prefix;
+	s->display_comment_prefix = 1;
+
+	/*
+	 * Most hints are counter-productive when the commit has
+	 * already started.
+	 */
+	s->hints = 0;
+
 	if (clean_message_contents)
 		stripspace(&sb, 0);
 
@@ -819,6 +840,7 @@
 	 */
 	if (!commitable && whence != FROM_MERGE && !allow_empty &&
 	    !(amend && is_a_merge(current_head))) {
+		s->display_comment_prefix = old_display_comment_prefix;
 		run_status(stdout, index_file, prefix, 0, s);
 		if (amend)
 			fputs(_(empty_amend_advice), stderr);
@@ -933,6 +955,7 @@
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
+	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -943,13 +966,17 @@
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
+	revs.mailmap = &mailmap;
+	read_mailmap(revs.mailmap, 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);
+		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+		clear_mailmap(&mailmap);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("No existing author found with '%s'"), name);
@@ -1180,6 +1207,10 @@
 		s->use_color = git_config_colorbool(k, v);
 		return 0;
 	}
+	if (!strcmp(k, "status.displaycommentprefix")) {
+		s->display_comment_prefix = git_config_bool(k, v);
+		return 0;
+	}
 	if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
 		int slot = parse_status_slot(k, 13);
 		if (slot < 0)
@@ -1244,10 +1275,7 @@
 	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);
+	status_init_config(&s, git_status_config);
 	argc = parse_options(argc, argv, prefix,
 			     builtin_status_options,
 			     builtin_status_usage, 0);
@@ -1489,11 +1517,8 @@
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_commit_usage, builtin_commit_options);
 
-	wt_status_prepare(&s);
-	gitmodules_config();
-	git_config(git_commit_config, &s);
+	status_init_config(&s, git_commit_config);
 	status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
-	determine_whence(&s);
 	s.colopts = 0;
 
 	if (get_sha1("HEAD", sha1))
@@ -1615,7 +1640,7 @@
 					   !current_head
 					   ? NULL
 					   : current_head->object.sha1,
-					   0);
+					   0, NULL);
 
 	nl = strchr(sb.buf, '\n');
 	if (nl)
diff --git a/builtin/config.c b/builtin/config.c
index fc8d882..20e89fe 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -119,7 +119,8 @@
 		must_print_delim = 1;
 	}
 	if (types == TYPE_INT)
-		sprintf(value, "%d", git_config_int(key_, value_ ? value_ : ""));
+		sprintf(value, "%"PRId64,
+			git_config_int64(key_, value_ ? value_ : ""));
 	else if (types == TYPE_BOOL)
 		vptr = git_config_bool(key_, value_) ? "true" : "false";
 	else if (types == TYPE_BOOL_OR_INT) {
@@ -268,8 +269,8 @@
 	else {
 		normalized = xmalloc(64);
 		if (types == TYPE_INT) {
-			int v = git_config_int(key, value);
-			sprintf(normalized, "%d", v);
+			int64_t v = git_config_int64(key, value);
+			sprintf(normalized, "%"PRId64, v);
 		}
 		else if (types == TYPE_BOOL)
 			sprintf(normalized, "%s",
diff --git a/builtin/describe.c b/builtin/describe.c
index c94e5c3..b9d3603 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -13,7 +13,7 @@
 #define MAX_TAGS	(FLAG_BITS - 1)
 
 static const char * const describe_usage[] = {
-	N_("git describe [options] <committish>*"),
+	N_("git describe [options] <commit-ish>*"),
 	N_("git describe [options] --dirty"),
 	NULL
 };
@@ -486,7 +486,7 @@
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
-		die(_("--dirty is incompatible with committishes"));
+		die(_("--dirty is incompatible with commit-ishes"));
 	} else {
 		while (argc-- > 0) {
 			describe(*argv++, argc == 0);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index b1b9b5e..78250ea 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -30,6 +30,7 @@
 static int use_done_feature;
 static int no_data;
 static int full_tree;
+static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
 				     const char *arg, int unset)
@@ -484,10 +485,32 @@
 	       (int)message_size, (int)message_size, message ? message : "");
 }
 
-static void get_tags_and_duplicates(struct rev_cmdline_info *info,
-				    struct string_list *extra_refs)
+static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
 {
-	struct tag *tag;
+	switch (e->item->type) {
+	case OBJ_COMMIT:
+		return (struct commit *)e->item;
+	case OBJ_TAG: {
+		struct 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);
+		return (struct commit *)tag;
+		break;
+	}
+	default:
+		return NULL;
+	}
+}
+
+static void get_tags_and_duplicates(struct rev_cmdline_info *info)
+{
 	int i;
 
 	for (i = 0; i < info->nr; i++) {
@@ -502,60 +525,45 @@
 		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:
-				export_blob(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:
+		commit = get_commit(e, full_name);
+		if (!commit) {
 			warning("%s: Unexpected object of type %s, skipping.",
 				e->name,
 				typename(e->item->type));
 			continue;
 		}
 
+		switch(commit->object.type) {
+		case OBJ_COMMIT:
+			break;
+		case OBJ_BLOB:
+			export_blob(commit->object.sha1);
+			continue;
+		default: /* OBJ_TAG (nested tags) is already handled */
+			warning("Tag points to object of unexpected type %s, skipping.",
+				typename(commit->object.type));
+			continue;
+		}
+
 		/*
 		 * This ref will not be updated through a commit, lets make
 		 * sure it gets properly updated eventually.
 		 */
 		if (commit->util || commit->object.flags & SHOWN)
-			string_list_append(extra_refs, full_name)->util = commit;
+			string_list_append(&extra_refs, full_name)->util = commit;
 		if (!commit->util)
 			commit->util = full_name;
 	}
 }
 
-static void handle_tags_and_duplicates(struct string_list *extra_refs)
+static void handle_tags_and_duplicates(void)
 {
 	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;
+	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);
@@ -657,7 +665,6 @@
 {
 	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;
 	uint32_t lastimportid;
@@ -709,7 +716,7 @@
 	if (import_filename && revs.prune_data.nr)
 		full_tree = 1;
 
-	get_tags_and_duplicates(&revs.cmdline, &extra_refs);
+	get_tags_and_duplicates(&revs.cmdline);
 
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
@@ -725,7 +732,7 @@
 		}
 	}
 
-	handle_tags_and_duplicates(&extra_refs);
+	handle_tags_and_duplicates();
 
 	if (export_filename && lastimportid != last_idnum)
 		export_marks(export_filename);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 9e654ef..bd7a101 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -262,7 +262,8 @@
 		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);
+				       check_old ? ref->old_sha1 : NULL,
+				       0, NULL);
 	if (!lock)
 		return errno == ENOTDIR ? STORE_REF_ERROR_DF_CONFLICT :
 					  STORE_REF_ERROR_OTHER;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 39fa5e8..97ce678 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -16,6 +16,7 @@
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
+#define HAS_OBJ   0x0004
 
 static int show_root;
 static int show_tags;
@@ -101,7 +102,7 @@
 	if (obj->flags & REACHABLE)
 		return 0;
 	obj->flags |= REACHABLE;
-	if (!obj->parsed) {
+	if (!(obj->flags & HAS_OBJ)) {
 		if (parent && !has_sha1_file(obj->sha1)) {
 			printf("broken link from %7s %s\n",
 				 typename(parent->type), sha1_to_hex(parent->sha1));
@@ -127,16 +128,13 @@
 	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;
-	}
+	if (tree)
+		free_tree_buffer(tree);
 	return result;
 }
 
@@ -178,7 +176,7 @@
 	 * except if it was in a pack-file and we didn't
 	 * do a full fsck
 	 */
-	if (!obj->parsed) {
+	if (!(obj->flags & HAS_OBJ)) {
 		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));
@@ -306,8 +304,7 @@
 	if (obj->type == OBJ_TREE) {
 		struct tree *item = (struct tree *) obj;
 
-		free(item->buffer);
-		item->buffer = NULL;
+		free_tree_buffer(item);
 	}
 
 	if (obj->type == OBJ_COMMIT) {
@@ -340,6 +337,7 @@
 		return error("%s: object corrupt or missing",
 			     sha1_to_hex(sha1));
 	}
+	obj->flags |= HAS_OBJ;
 	return fsck_obj(obj);
 }
 
@@ -352,6 +350,7 @@
 		errors_found |= ERROR_OBJECT;
 		return error("%s: object corrupt or missing", sha1_to_hex(sha1));
 	}
+	obj->flags = HAS_OBJ;
 	return fsck_obj(obj);
 }
 
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 9c1cfac..9e9eb4b 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -770,6 +770,7 @@
 			if (obj->type == OBJ_TREE) {
 				struct tree *item = (struct tree *) obj;
 				item->buffer = NULL;
+				obj->parsed = 0;
 			}
 			if (obj->type == OBJ_COMMIT) {
 				struct commit *commit = (struct commit *) obj;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index d4823c9..e1cf6d8 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -220,6 +220,8 @@
 
 	/* For cached/deleted files we don't need to even do the readdir */
 	if (show_others || show_killed) {
+		if (!show_others)
+			dir->flags |= DIR_COLLECT_KILLED_ONLY;
 		fill_directory(dir, &pathspec);
 		if (show_others)
 			show_other_files(dir);
diff --git a/builtin/merge.c b/builtin/merge.c
index a8cf4a2..02a69c1 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1193,7 +1193,7 @@
 	 * 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.
+	 * commit-ish 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.
 	 */
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 4eb0521..e86cd57 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -2378,7 +2378,7 @@
 
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
-	mark_edges_uninteresting(revs.commits, &revs, show_edge);
+	mark_edges_uninteresting(&revs, show_edge);
 	traverse_commit_list(&revs, show_commit, show_object, NULL);
 
 	if (keep_unreachable)
diff --git a/builtin/push.c b/builtin/push.c
index 50bbfd6..7b1b66c 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -15,7 +15,7 @@
 	NULL,
 };
 
-static int thin;
+static int thin = 1;
 static int deleterefs;
 static const char *receivepack;
 static int verbosity;
@@ -315,8 +315,7 @@
 	if (receivepack)
 		transport_set_option(transport,
 				     TRANS_OPT_RECEIVEPACK, receivepack);
-	if (thin)
-		transport_set_option(transport, TRANS_OPT_THIN, "yes");
+	transport_set_option(transport, TRANS_OPT_THIN, thin ? "yes" : NULL);
 
 	if (!is_empty_cas(&cas)) {
 		if (!transport->smart_options)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 7434d9b..67ce1ef 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -39,6 +39,7 @@
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
+static int fix_thin = 1;
 static const char *head_name;
 static void *head_name_to_free;
 static int sent_capabilities;
@@ -525,7 +526,8 @@
 		return NULL; /* good */
 	}
 	else {
-		lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
+		lock = lock_any_ref_for_update(namespaced_name, old_sha1,
+					       0, NULL);
 		if (!lock) {
 			rp_error("failed to lock %s", name);
 			return "failed to lock";
@@ -870,7 +872,8 @@
 		keeper[i++] = "--stdin";
 		if (fsck_objects)
 			keeper[i++] = "--strict";
-		keeper[i++] = "--fix-thin";
+		if (fix_thin)
+			keeper[i++] = "--fix-thin";
 		keeper[i++] = hdr_arg;
 		keeper[i++] = keep_arg;
 		keeper[i++] = NULL;
@@ -976,6 +979,10 @@
 				stateless_rpc = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--reject-thin-pack-for-testing")) {
+				fix_thin = 0;
+				continue;
+			}
 
 			usage(receive_pack_usage);
 		}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 54184b3..6eb24c8 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -94,8 +94,7 @@
 			complete = 0;
 		}
 	}
-	free(tree->buffer);
-	tree->buffer = NULL;
+	free_tree_buffer(tree);
 
 	if (complete)
 		tree->object.flags |= SEEN;
@@ -366,7 +365,7 @@
 	 * we take the lock for the ref itself to prevent it from
 	 * getting updated.
 	 */
-	lock = lock_any_ref_for_update(ref, sha1, 0);
+	lock = lock_any_ref_for_update(ref, sha1, 0, NULL);
 	if (!lock)
 		return error("cannot lock ref '%s'", ref);
 	log_file = git_pathdup("logs/%s", ref);
diff --git a/builtin/replace.c b/builtin/replace.c
index 11b0a55..301b45c 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -105,7 +105,7 @@
 	else if (!force)
 		die("replace ref '%s' already exists", ref);
 
-	lock = lock_any_ref_for_update(ref, prev, 0);
+	lock = lock_any_ref_for_update(ref, prev, 0, NULL);
 	if (!lock)
 		die("%s: cannot lock the ref", ref);
 	if (write_ref_sha1(lock, repl, NULL) < 0)
diff --git a/builtin/reset.c b/builtin/reset.c
index 5e4c551..1a53448 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -226,7 +226,7 @@
 		       prefix, argv);
 }
 
-static int update_refs(const char *rev, const unsigned char *sha1)
+static int reset_refs(const char *rev, const unsigned char *sha1)
 {
 	int update_ref_status;
 	struct strbuf msg = STRBUF_INIT;
@@ -330,11 +330,14 @@
 		die_if_unmerged_cache(reset_type);
 
 	if (reset_type != SOFT) {
-		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		struct lock_file *lock = xcalloc(1, sizeof(*lock));
 		int newfd = hold_locked_index(lock, 1);
 		if (reset_type == MIXED) {
+			int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
 			if (read_from_tree(&pathspec, sha1))
 				return 1;
+			refresh_index(&the_index, flags, NULL, NULL,
+				      _("Unstaged changes after reset:"));
 		} else {
 			int err = reset_index(sha1, reset_type, quiet);
 			if (reset_type == KEEP && !err)
@@ -343,12 +346,6 @@
 				die(_("Could not reset index file to revision '%s'."), rev);
 		}
 
-		if (reset_type == MIXED) { /* Report what has not been updated. */
-			int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
-			refresh_index(&the_index, flags, NULL, NULL,
-				      _("Unstaged changes after reset:"));
-		}
-
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock))
 			die(_("Could not write new index file."));
@@ -357,7 +354,7 @@
 	if (!pathspec.nr && !unborn) {
 		/* Any resets without paths update HEAD to the head being
 		 * switched to, saving the previous head in ORIG_HEAD before. */
-		update_ref_status = update_refs(rev, sha1);
+		update_ref_status = reset_refs(rev, sha1);
 
 		if (reset_type == HARD && !update_ref_status && !quiet)
 			print_new_head_line(lookup_commit_reference(sha1));
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index a5ec30d..4fc1616 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -336,7 +336,7 @@
 	if (prepare_revision_walk(&revs))
 		die("revision walk setup failed");
 	if (revs.tree_objects)
-		mark_edges_uninteresting(revs.commits, &revs, show_edge);
+		mark_edges_uninteresting(&revs, show_edge);
 
 	if (bisect_list) {
 		int reaches = reaches, all = all;
diff --git a/builtin/revert.c b/builtin/revert.c
index 8e87acd..52c35e7 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -202,6 +202,8 @@
 	memset(&opts, 0, sizeof(opts));
 	opts.action = REPLAY_PICK;
 	git_config(git_default_config, NULL);
+	if (!strcmp(argv[1], "-"))
+		argv[1] = "@{-1}";
 	parse_args(argc, argv, &opts);
 	res = sequencer_pick_revisions(&opts);
 	if (res < 0)
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index ae73d17..c226f76 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -127,9 +127,11 @@
 			author = buffer + 7;
 		buffer = eol;
 	}
-	if (!author)
-		die(_("Missing author: %s"),
+	if (!author) {
+		warning(_("Missing author: %s"),
 		    sha1_to_hex(commit->object.sha1));
+		return;
+	}
 	if (log->user_format) {
 		struct pretty_print_context ctx = {0};
 		ctx.fmt = CMIT_FMT_USERFORMAT;
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index e981dfb..1259ed7 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -89,11 +89,11 @@
 
 	if (argc == 2) {
 		if (!strcmp(argv[1], "-s") ||
-			!strcmp(argv[1], "--strip-comments")) {
-			 strip_comments = 1;
+		    !strcmp(argv[1], "--strip-comments")) {
+			strip_comments = 1;
 		} else if (!strcmp(argv[1], "-c") ||
-					 !strcmp(argv[1], "--comment-lines")) {
-			 mode = COMMENT_LINES;
+			   !strcmp(argv[1], "--comment-lines")) {
+			mode = COMMENT_LINES;
 		} else {
 			mode = INVAL;
 		}
diff --git a/builtin/tag.c b/builtin/tag.c
index b577af5..ea55f1d 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -574,7 +574,7 @@
 	if (annotate)
 		create_tag(object, tag, &buf, &opt, prev, object);
 
-	lock = lock_any_ref_for_update(ref.buf, prev, 0);
+	lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL);
 	if (!lock)
 		die(_("%s: cannot lock the ref"), ref.buf);
 	if (write_ref_sha1(lock, object, NULL) < 0)
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 7484d36..702e90d 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -2,23 +2,261 @@
 #include "refs.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "quote.h"
+#include "argv-array.h"
 
 static const char * const git_update_ref_usage[] = {
 	N_("git update-ref [options] -d <refname> [<oldval>]"),
 	N_("git update-ref [options]    <refname> <newval> [<oldval>]"),
+	N_("git update-ref [options] --stdin [-z]"),
 	NULL
 };
 
+static int updates_alloc;
+static int updates_count;
+static const struct ref_update **updates;
+
+static char line_termination = '\n';
+static int update_flags;
+
+static struct ref_update *update_alloc(void)
+{
+	struct ref_update *update;
+
+	/* Allocate and zero-init a struct ref_update */
+	update = xcalloc(1, sizeof(*update));
+	ALLOC_GROW(updates, updates_count + 1, updates_alloc);
+	updates[updates_count++] = update;
+
+	/* Store and reset accumulated options */
+	update->flags = update_flags;
+	update_flags = 0;
+
+	return update;
+}
+
+static void update_store_ref_name(struct ref_update *update,
+				  const char *ref_name)
+{
+	if (check_refname_format(ref_name, REFNAME_ALLOW_ONELEVEL))
+		die("invalid ref format: %s", ref_name);
+	update->ref_name = xstrdup(ref_name);
+}
+
+static void update_store_new_sha1(struct ref_update *update,
+				  const char *newvalue)
+{
+	if (*newvalue && get_sha1(newvalue, update->new_sha1))
+		die("invalid new value for ref %s: %s",
+		    update->ref_name, newvalue);
+}
+
+static void update_store_old_sha1(struct ref_update *update,
+				  const char *oldvalue)
+{
+	if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
+		die("invalid old value for ref %s: %s",
+		    update->ref_name, oldvalue);
+
+	/* We have an old value if non-empty, or if empty without -z */
+	update->have_old = *oldvalue || line_termination;
+}
+
+static const char *parse_arg(const char *next, struct strbuf *arg)
+{
+	/* Parse SP-terminated, possibly C-quoted argument */
+	if (*next != '"')
+		while (*next && !isspace(*next))
+			strbuf_addch(arg, *next++);
+	else if (unquote_c_style(arg, next, &next))
+		die("badly quoted argument: %s", next);
+
+	/* Return position after the argument */
+	return next;
+}
+
+static const char *parse_first_arg(const char *next, struct strbuf *arg)
+{
+	/* Parse argument immediately after "command SP" */
+	strbuf_reset(arg);
+	if (line_termination) {
+		/* Without -z, use the next argument */
+		next = parse_arg(next, arg);
+	} else {
+		/* With -z, use rest of first NUL-terminated line */
+		strbuf_addstr(arg, next);
+		next = next + arg->len;
+	}
+	return next;
+}
+
+static const char *parse_next_arg(const char *next, struct strbuf *arg)
+{
+	/* Parse next SP-terminated or NUL-terminated argument, if any */
+	strbuf_reset(arg);
+	if (line_termination) {
+		/* Without -z, consume SP and use next argument */
+		if (!*next)
+			return NULL;
+		if (*next != ' ')
+			die("expected SP but got: %s", next);
+		next = parse_arg(next + 1, arg);
+	} else {
+		/* With -z, read the next NUL-terminated line */
+		if (*next)
+			die("expected NUL but got: %s", next);
+		if (strbuf_getline(arg, stdin, '\0') == EOF)
+			return NULL;
+		next = arg->buf + arg->len;
+	}
+	return next;
+}
+
+static void parse_cmd_update(const char *next)
+{
+	struct strbuf ref = STRBUF_INIT;
+	struct strbuf newvalue = STRBUF_INIT;
+	struct strbuf oldvalue = STRBUF_INIT;
+	struct ref_update *update;
+
+	update = update_alloc();
+
+	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+		update_store_ref_name(update, ref.buf);
+	else
+		die("update line missing <ref>");
+
+	if ((next = parse_next_arg(next, &newvalue)) != NULL)
+		update_store_new_sha1(update, newvalue.buf);
+	else
+		die("update %s missing <newvalue>", ref.buf);
+
+	if ((next = parse_next_arg(next, &oldvalue)) != NULL)
+		update_store_old_sha1(update, oldvalue.buf);
+	else if(!line_termination)
+		die("update %s missing [<oldvalue>] NUL", ref.buf);
+
+	if (next && *next)
+		die("update %s has extra input: %s", ref.buf, next);
+}
+
+static void parse_cmd_create(const char *next)
+{
+	struct strbuf ref = STRBUF_INIT;
+	struct strbuf newvalue = STRBUF_INIT;
+	struct ref_update *update;
+
+	update = update_alloc();
+
+	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+		update_store_ref_name(update, ref.buf);
+	else
+		die("create line missing <ref>");
+
+	if ((next = parse_next_arg(next, &newvalue)) != NULL)
+		update_store_new_sha1(update, newvalue.buf);
+	else
+		die("create %s missing <newvalue>", ref.buf);
+	if (is_null_sha1(update->new_sha1))
+		die("create %s given zero new value", ref.buf);
+
+	if (next && *next)
+		die("create %s has extra input: %s", ref.buf, next);
+}
+
+static void parse_cmd_delete(const char *next)
+{
+	struct strbuf ref = STRBUF_INIT;
+	struct strbuf oldvalue = STRBUF_INIT;
+	struct ref_update *update;
+
+	update = update_alloc();
+
+	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+		update_store_ref_name(update, ref.buf);
+	else
+		die("delete line missing <ref>");
+
+	if ((next = parse_next_arg(next, &oldvalue)) != NULL)
+		update_store_old_sha1(update, oldvalue.buf);
+	else if(!line_termination)
+		die("delete %s missing [<oldvalue>] NUL", ref.buf);
+	if (update->have_old && is_null_sha1(update->old_sha1))
+		die("delete %s given zero old value", ref.buf);
+
+	if (next && *next)
+		die("delete %s has extra input: %s", ref.buf, next);
+}
+
+static void parse_cmd_verify(const char *next)
+{
+	struct strbuf ref = STRBUF_INIT;
+	struct strbuf value = STRBUF_INIT;
+	struct ref_update *update;
+
+	update = update_alloc();
+
+	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+		update_store_ref_name(update, ref.buf);
+	else
+		die("verify line missing <ref>");
+
+	if ((next = parse_next_arg(next, &value)) != NULL) {
+		update_store_old_sha1(update, value.buf);
+		update_store_new_sha1(update, value.buf);
+	} else if(!line_termination)
+		die("verify %s missing [<oldvalue>] NUL", ref.buf);
+
+	if (next && *next)
+		die("verify %s has extra input: %s", ref.buf, next);
+}
+
+static void parse_cmd_option(const char *next)
+{
+	if (!strcmp(next, "no-deref"))
+		update_flags |= REF_NODEREF;
+	else
+		die("option unknown: %s", next);
+}
+
+static void update_refs_stdin(void)
+{
+	struct strbuf cmd = STRBUF_INIT;
+
+	/* Read each line dispatch its command */
+	while (strbuf_getline(&cmd, stdin, line_termination) != EOF)
+		if (!cmd.buf[0])
+			die("empty command in input");
+		else if (isspace(*cmd.buf))
+			die("whitespace before command: %s", cmd.buf);
+		else if (!prefixcmp(cmd.buf, "update "))
+			parse_cmd_update(cmd.buf + 7);
+		else if (!prefixcmp(cmd.buf, "create "))
+			parse_cmd_create(cmd.buf + 7);
+		else if (!prefixcmp(cmd.buf, "delete "))
+			parse_cmd_delete(cmd.buf + 7);
+		else if (!prefixcmp(cmd.buf, "verify "))
+			parse_cmd_verify(cmd.buf + 7);
+		else if (!prefixcmp(cmd.buf, "option "))
+			parse_cmd_option(cmd.buf + 7);
+		else
+			die("unknown command: %s", cmd.buf);
+
+	strbuf_release(&cmd);
+}
+
 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;
+	int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
 	struct option options[] = {
 		OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
 		OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
 		OPT_BOOL( 0 , "no-deref", &no_deref,
 					N_("update <refname> not the one it points to")),
+		OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
+		OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
 		OPT_END(),
 	};
 
@@ -28,6 +266,18 @@
 	if (msg && !*msg)
 		die("Refusing to perform update with empty message.");
 
+	if (read_stdin) {
+		if (delete || no_deref || argc > 0)
+			usage_with_options(git_update_ref_usage, options);
+		if (end_null)
+			line_termination = '\0';
+		update_refs_stdin();
+		return update_refs(msg, updates, updates_count, DIE_ON_ERR);
+	}
+
+	if (end_null)
+		usage_with_options(git_update_ref_usage, options);
+
 	if (delete) {
 		if (argc < 1 || argc > 2)
 			usage_with_options(git_update_ref_usage, options);
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 6b0b6d4..118c625 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -114,7 +114,7 @@
 
 		if (size && !s.avail_in) {
 			ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
-			if (xread(fd, ibuf, rsize) != rsize)
+			if (read_in_full(fd, ibuf, rsize) != rsize)
 				die("failed to read %d bytes from '%s'",
 				    (int)rsize, path);
 			offset += rsize;
diff --git a/cache.h b/cache.h
index 9ef778a..51d6602 100644
--- a/cache.h
+++ b/cache.h
@@ -880,7 +880,7 @@
 
 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 interpret_branch_name(const char *str, int len, 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);
@@ -1115,6 +1115,7 @@
 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 int64_t git_config_int64(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 *);
diff --git a/commit.h b/commit.h
index 90a5a3c..bd841f4 100644
--- a/commit.h
+++ b/commit.h
@@ -201,6 +201,10 @@
 		int depth, int shallow_flag, int not_shallow_flag);
 extern void check_shallow_file_for_update(void);
 extern void set_alternate_shallow_file(const char *path);
+extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
+extern void setup_alternate_shallow(struct lock_file *shallow_lock,
+				    const char **alternate_shallow_file);
+extern char *setup_temporary_shallow(void);
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
diff --git a/compat/mingw.h b/compat/mingw.h
index 19d82de..92cd728 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -344,6 +344,7 @@
 #define find_last_dir_sep mingw_find_last_dir_sep
 #define PATH_SEP ';'
 #define PRIuMAX "I64u"
+#define PRId64 "I64d"
 
 void mingw_open_html(const char *path);
 #define open_html mingw_open_html
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 7980abd..95fe849 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -48,11 +48,8 @@
 	if (output_fd >= 0) {
 		close(output_fd);
 		strcpy(path + len, auml_nfd);
-		/* Indicate to the user, that we can configure it to true */
-		if (!access(path, R_OK))
-			git_config_set("core.precomposeunicode", "false");
-		/* To be backward compatible, set precomposed_unicode to 0 */
-		precomposed_unicode = 0;
+		precomposed_unicode = access(path, R_OK) ? 0 : 1;
+		git_config_set("core.precomposeunicode", precomposed_unicode ? "true" : "false");
 		strcpy(path + len, auml_nfc);
 		if (unlink(path))
 			die_errno(_("failed to unlink '%s'"), path);
diff --git a/config.c b/config.c
index 9f9bf0c..6588cf5 100644
--- a/config.c
+++ b/config.c
@@ -468,7 +468,7 @@
 	return 0;
 }
 
-static int git_parse_long(const char *value, long *ret)
+static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
 {
 	if (value && *value) {
 		char *end;
@@ -480,21 +480,25 @@
 		val = strtoimax(value, &end, 0);
 		if (errno == ERANGE)
 			return 0;
-		if (!parse_unit_factor(end, &factor))
+		if (!parse_unit_factor(end, &factor)) {
+			errno = EINVAL;
 			return 0;
+		}
 		uval = abs(val);
 		uval *= factor;
-		if ((uval > maximum_signed_value_of_type(long)) ||
-		    (abs(val) > uval))
+		if (uval > max || abs(val) > uval) {
+			errno = ERANGE;
 			return 0;
+		}
 		val *= factor;
 		*ret = val;
 		return 1;
 	}
+	errno = EINVAL;
 	return 0;
 }
 
-int git_parse_ulong(const char *value, unsigned long *ret)
+int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
 {
 	if (value && *value) {
 		char *end;
@@ -506,29 +510,75 @@
 		if (errno == ERANGE)
 			return 0;
 		oldval = val;
-		if (!parse_unit_factor(end, &val))
+		if (!parse_unit_factor(end, &val)) {
+			errno = EINVAL;
 			return 0;
-		if ((val > maximum_unsigned_value_of_type(long)) ||
-		    (oldval > val))
+		}
+		if (val > max || oldval > val) {
+			errno = ERANGE;
 			return 0;
+		}
 		*ret = val;
 		return 1;
 	}
+	errno = EINVAL;
 	return 0;
 }
 
-static void die_bad_config(const char *name)
+static int git_parse_int(const char *value, int *ret)
 {
+	intmax_t tmp;
+	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+static int git_parse_int64(const char *value, int64_t *ret)
+{
+	intmax_t tmp;
+	if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+	uintmax_t tmp;
+	if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+		return 0;
+	*ret = tmp;
+	return 1;
+}
+
+static void die_bad_number(const char *name, const char *value)
+{
+	const char *reason = errno == ERANGE ?
+			     "out of range" :
+			     "invalid unit";
+	if (!value)
+		value = "";
+
 	if (cf && cf->name)
-		die("bad config value for '%s' in %s", name, cf->name);
-	die("bad config value for '%s'", name);
+		die("bad numeric config value '%s' for '%s' in %s: %s",
+		    value, name, cf->name, reason);
+	die("bad numeric config value '%s' for '%s': %s", value, name, reason);
 }
 
 int git_config_int(const char *name, const char *value)
 {
-	long ret = 0;
-	if (!git_parse_long(value, &ret))
-		die_bad_config(name);
+	int ret;
+	if (!git_parse_int(value, &ret))
+		die_bad_number(name, value);
+	return ret;
+}
+
+int64_t git_config_int64(const char *name, const char *value)
+{
+	int64_t ret;
+	if (!git_parse_int64(value, &ret))
+		die_bad_number(name, value);
 	return ret;
 }
 
@@ -536,7 +586,7 @@
 {
 	unsigned long ret;
 	if (!git_parse_ulong(value, &ret))
-		die_bad_config(name);
+		die_bad_number(name, value);
 	return ret;
 }
 
@@ -559,10 +609,10 @@
 
 int git_config_maybe_bool(const char *name, const char *value)
 {
-	long v = git_config_maybe_bool_text(name, value);
+	int v = git_config_maybe_bool_text(name, value);
 	if (0 <= v)
 		return v;
-	if (git_parse_long(value, &v))
+	if (git_parse_int(value, &v))
 		return !!v;
 	return -1;
 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e1b7313..86f7734 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -901,7 +901,7 @@
 	esac
 
 	# XXX should we check for --update and --all options ?
-	__git_complete_index_file "--others --modified"
+	__git_complete_index_file "--others --modified --directory --no-empty-directory"
 }
 
 _git_archive ()
@@ -1063,7 +1063,7 @@
 	esac
 
 	# XXX should we check for -x option ?
-	__git_complete_index_file "--others"
+	__git_complete_index_file "--others --directory"
 }
 
 _git_clone ()
diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
index fb6429b..428cc1a 100755
--- a/contrib/contacts/git-contacts
+++ b/contrib/contacts/git-contacts
@@ -181,6 +181,10 @@
 	scan_rev_args(\%sources, \@rev_args)
 }
 
+my $toplevel = `git rev-parse --show-toplevel`;
+chomp $toplevel;
+chdir($toplevel) or die "chdir failure: $toplevel: $!\n";
+
 my %commits;
 blame_sources(\%sources, \%commits);
 import_commits(\%commits);
diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh
index 7b922c3..a5e42a9 100755
--- a/contrib/examples/git-merge.sh
+++ b/contrib/examples/git-merge.sh
@@ -263,7 +263,7 @@
 
 # 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
+# people might have misused the interface and used a commit-ish 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.
 
diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email
index 1531150..8ee410f 100755
--- a/contrib/hooks/post-receive-email
+++ b/contrib/hooks/post-receive-email
@@ -242,6 +242,9 @@
 	cat <<-EOF
 	To: $recipients
 	Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
+	MIME-Version: 1.0
+	Content-Type: text/plain; charset=utf-8
+	Content-Transfer-Encoding: 8bit
 	X-Git-Refname: $refname
 	X-Git-Reftype: $refname_type
 	X-Git-Oldrev: $oldrev
@@ -471,7 +474,7 @@
 	echo "       was  $oldrev"
 	echo ""
 	echo $LOGBEGIN
-	git show -s --pretty=oneline $oldrev
+	git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
 	echo $LOGEND
 }
 
@@ -547,11 +550,11 @@
 		# performed on them
 		if [ -n "$prevtag" ]; then
 			# Show changes since the previous release
-			git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
+			git shortlog "$prevtag..$newrev"
 		else
 			# No previous tag, show all the changes since time
 			# began
-			git rev-list --pretty=short $newrev | git shortlog
+			git shortlog $newrev
 		fi
 		;;
 	*)
@@ -571,7 +574,7 @@
 	echo "       was  $oldrev"
 	echo ""
 	echo $LOGBEGIN
-	git show -s --pretty=oneline $oldrev
+	git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
 	echo $LOGEND
 }
 
@@ -617,7 +620,7 @@
 	echo ""
 	if [ "$newrev_type" = "commit" ]; then
 		echo $LOGBEGIN
-		git show --no-color --root -s --pretty=medium $newrev
+		git diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev
 		echo $LOGEND
 	else
 		# What can we do here?  The tag marks an object that is not
@@ -636,7 +639,7 @@
 	echo "       was  $oldrev"
 	echo ""
 	echo $LOGBEGIN
-	git show -s --pretty=oneline $oldrev
+	git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev
 	echo $LOGEND
 }
 
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
index 76fcd4d..f206f96 100644
--- a/contrib/mw-to-git/Makefile
+++ b/contrib/mw-to-git/Makefile
@@ -24,6 +24,11 @@
 
 all: build
 
+test: all
+	$(MAKE) -C t
+
+check: perlcritic test
+
 install_pm:
 	install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
 
@@ -41,4 +46,7 @@
 	rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
 
 perlcritic:
-	perlcritic -2 *.perl
+	perlcritic -5 $(SCRIPT_PERL)
+	-perlcritic -2 $(SCRIPT_PERL)
+
+.PHONY: all test check install_pm install clean perlcritic
diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl
index f8d7d2c..476e0a2 100755
--- a/contrib/mw-to-git/git-remote-mediawiki.perl
+++ b/contrib/mw-to-git/git-remote-mediawiki.perl
@@ -590,6 +590,9 @@
 	print {*STDOUT} "import\n";
 	print {*STDOUT} "list\n";
 	print {*STDOUT} "push\n";
+	if ($dumb_push) {
+		print {*STDOUT} "no-private-update\n";
+	}
 	print {*STDOUT} "\n";
 	return;
 }
@@ -622,6 +625,9 @@
 		rvstartid => $fetch_from,
 		rvlimit => 500,
 		pageids => $id,
+
+		# Let MediaWiki know that we support the latest API.
+		continue => '',
 	};
 
 	my $revnum = 0;
@@ -637,8 +643,15 @@
 			push(@page_revs, $page_rev_ids);
 			$revnum++;
 		}
-		last if (!$result->{'query-continue'});
-		$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
+
+		if ($result->{'query-continue'}) { # For legacy APIs
+			$query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
+		} elsif ($result->{continue}) { # For newer APIs
+			$query->{rvstartid} = $result->{continue}->{rvcontinue};
+			$query->{continue} = $result->{continue}->{continue};
+		} else {
+			last;
+		}
 	}
 	if ($shallow_import && @page_revs) {
 		print {*STDERR} "  Found 1 revision (shallow import).\n";
@@ -1211,7 +1224,6 @@
 		}
 		if (!$dumb_push) {
 			run_git(qq(notes --ref=${remotename}/mediawiki add -f -m "mediawiki_revision: ${mw_revision}" ${sha1_commit}));
-			run_git(qq(update-ref -m "Git-MediaWiki push" refs/mediawiki/${remotename}/master ${sha1_commit} ${sha1_child}));
 		}
 	}
 
diff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh
new file mode 100755
index 0000000..27e267f
--- /dev/null
+++ b/contrib/mw-to-git/t/t9365-continuing-queries.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='Test the Git Mediawiki remote helper: queries w/ more than 500 results'
+
+. ./test-gitmw-lib.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+test_check_precond
+
+test_expect_success 'creating page w/ >500 revisions' '
+	wiki_reset &&
+	for i in `test_seq 501`
+	do
+		echo "creating revision $i" &&
+		wiki_editpage foo "revision $i<br/>" true
+	done
+'
+
+test_expect_success 'cloning page w/ >500 revisions' '
+	git clone mediawiki::'"$WIKI_URL"' mw_dir
+'
+
+test_done
diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr
index 1e0044b..054161a 100755
--- a/contrib/remote-helpers/git-remote-bzr
+++ b/contrib/remote-helpers/git-remote-bzr
@@ -13,8 +13,11 @@
 # or
 # % git clone bzr::lp:myrepo
 #
-# If you want to specify which branches you want track (per repo):
-# git config remote-bzr.branches 'trunk, devel, test'
+# If you want to specify which branches you want to track (per repo):
+# % git config remote.origin.bzr-branches 'trunk, devel, test'
+#
+# Where 'origin' is the name of the repository you want to specify the
+# branches.
 #
 
 import sys
@@ -168,17 +171,16 @@
         if not m:
             return None
         _, name, email, date, tz = m.groups()
+        name = name.decode('utf-8')
         committer = '%s <%s>' % (name, email)
         tz = int(tz)
         tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
         return (committer, int(date), tz)
 
 def rev_to_mark(rev):
-    global marks
     return marks.from_rev(rev)
 
 def mark_to_rev(mark):
-    global marks
     return marks.to_rev(mark)
 
 def fixup_user(user):
@@ -233,8 +235,6 @@
     return modified, removed
 
 def export_files(tree, files):
-    global marks, filenodes
-
     final = []
     for path, fid in files.iteritems():
         kind = tree.kind(fid)
@@ -276,8 +276,6 @@
     return final
 
 def export_branch(repo, name):
-    global prefix
-
     ref = '%s/heads/%s' % (prefix, name)
     tip = marks.get_tip(name)
 
@@ -378,16 +376,12 @@
     marks.set_tip(name, revid)
 
 def export_tag(repo, name):
-    global tags, prefix
-
     ref = '%s/tags/%s' % (prefix, name)
     print "reset %s" % ref
     print "from :%u" % rev_to_mark(tags[name])
     print
 
 def do_import(parser):
-    global dirname
-
     repo = parser.repo
     path = os.path.join(dirname, 'marks-git')
 
@@ -413,8 +407,6 @@
     sys.stdout.flush()
 
 def parse_blob(parser):
-    global blob_marks
-
     parser.next()
     mark = parser.get_mark()
     parser.next()
@@ -425,8 +417,6 @@
 class CustomTree():
 
     def __init__(self, branch, revid, parents, files):
-        global files_cache
-
         self.updates = {}
         self.branch = branch
 
@@ -484,7 +474,7 @@
             add_entry(fid, dirname, 'directory')
             return fid
 
-        def add_entry(fid, path, kind, mode = None):
+        def add_entry(fid, path, kind, mode=None):
             dirname, basename = os.path.split(path)
             parent_fid = get_parent(dirname, basename)
 
@@ -505,7 +495,7 @@
             self.files[path] = [change[0], None]
             changes.append(change)
 
-        def update_entry(fid, path, kind, mode = None):
+        def update_entry(fid, path, kind, mode=None):
             dirname, basename = os.path.split(path)
             parent_fid = get_parent(dirname, basename)
 
@@ -583,9 +573,6 @@
     return string
 
 def parse_commit(parser):
-    global marks, blob_marks, parsed_refs
-    global mode
-
     parents = []
 
     ref = parser[1]
@@ -657,8 +644,6 @@
     marks.new_mark(revid, commit_mark)
 
 def parse_reset(parser):
-    global parsed_refs
-
     ref = parser[1]
     parser.next()
 
@@ -674,8 +659,6 @@
     parsed_refs[ref] = mark_to_rev(from_mark)
 
 def do_export(parser):
-    global parsed_refs, dirname, transports
-
     parser.next()
 
     for line in parser.each_block('done'):
@@ -725,8 +708,6 @@
     print
 
 def do_capabilities(parser):
-    global dirname
-
     print "import"
     print "export"
     print "refspec refs/heads/*:%s/heads/*" % prefix
@@ -744,8 +725,6 @@
     return not True in [c in name for c in '~^: \\']
 
 def do_list(parser):
-    global tags
-
     master_branch = None
 
     for name in branches:
@@ -770,7 +749,6 @@
     print
 
 def clone(path, remote_branch):
-    global transports
     try:
         bdir = bzrlib.bzrdir.BzrDir.create(path, possible_transports=transports)
     except bzrlib.errors.AlreadyControlDirError:
@@ -780,8 +758,6 @@
     return remote_branch.sprout(bdir, repository=repo)
 
 def get_remote_branch(name):
-    global dirname, branches, transports
-
     remote_branch = bzrlib.branch.Branch.open(branches[name],
                                               possible_transports=transports)
     if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
@@ -825,8 +801,6 @@
             yield name, branch.base
 
 def get_repo(url, alias):
-    global dirname, peer, branches, transports
-
     normal_url = bzrlib.urlutils.normalize_url(url)
     origin = bzrlib.bzrdir.BzrDir.open(url, possible_transports=transports)
     is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
@@ -858,9 +832,13 @@
             except bzrlib.errors.NoRepositoryPresent:
                 pass
 
-    wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+    wanted = get_config('remote.%s.bzr-branches' % alias).rstrip().split(', ')
     # stupid python
     wanted = [e for e in wanted if e]
+    if not wanted:
+        wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+        # stupid python
+        wanted = [e for e in wanted if e]
 
     if not wanted:
         try:
diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg
index c276039..92d994e 100755
--- a/contrib/remote-helpers/git-remote-hg
+++ b/contrib/remote-helpers/git-remote-hg
@@ -23,8 +23,12 @@
 import urllib
 import atexit
 import urlparse, hashlib
+import time as ptime
 
 #
+# If you want to see Mercurial revisions as Git commit notes:
+# git config core.notesRef refs/notes/hg
+#
 # If you are not in hg-git-compat mode and want to disable the tracking of
 # named branches:
 # git config --global remote-hg.track-branches false
@@ -126,6 +130,7 @@
         self.rev_marks = {}
         self.last_mark = 0
         self.version = 0
+        self.last_note = 0
 
     def load(self):
         if not os.path.exists(self.path):
@@ -137,6 +142,7 @@
         self.marks = tmp['marks']
         self.last_mark = tmp['last-mark']
         self.version = tmp.get('version', 1)
+        self.last_note = tmp.get('last-note', 0)
 
         for rev, mark in self.marks.iteritems():
             self.rev_marks[mark] = rev
@@ -150,7 +156,7 @@
         self.version = 2
 
     def dict(self):
-        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version }
+        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version, 'last-note' : self.last_note }
 
     def store(self):
         json.dump(self.dict(), open(self.path, 'w'))
@@ -227,8 +233,6 @@
         return sys.stdin.read(size)
 
     def get_author(self):
-        global bad_mail
-
         ex = None
         m = RAW_AUTHOR_RE.match(self.line)
         if not m:
@@ -261,8 +265,6 @@
     return os.path.relpath(path, '/')
 
 def export_files(files):
-    global marks, filenodes
-
     final = []
     for f in files:
         fid = node.hex(f.filenode())
@@ -344,8 +346,6 @@
     return (name, mail)
 
 def fixup_user(user):
-    global mode, bad_mail
-
     if mode == 'git':
         name, mail = fixup_user_git(user)
     else:
@@ -374,7 +374,7 @@
         bookmarks.write(repo)
 
 def get_repo(url, alias):
-    global dirname, peer
+    global peer
 
     myui = ui.ui()
     myui.setconfig('ui', 'interactive', 'off')
@@ -429,16 +429,12 @@
     return repo
 
 def rev_to_mark(rev):
-    global marks
     return marks.from_rev(rev.hex())
 
 def mark_to_rev(mark):
-    global marks
     return marks.to_rev(mark)
 
 def export_ref(repo, name, kind, head):
-    global prefix, marks, mode
-
     ename = '%s/%s' % (kind, name)
     try:
         tip = marks.get_tip(ename)
@@ -535,6 +531,31 @@
     print "from :%u" % rev_to_mark(head)
     print
 
+    pending_revs = set(revs) - notes
+    if pending_revs:
+        note_mark = marks.next_mark()
+        ref = "refs/notes/hg"
+
+        print "commit %s" % ref
+        print "mark :%d" % (note_mark)
+        print "committer remote-hg <> %s" % (ptime.strftime('%s %z'))
+        desc = "Notes for %s\n" % (name)
+        print "data %d" % (len(desc))
+        print desc
+        if marks.last_note:
+            print "from :%u" % marks.last_note
+
+        for rev in pending_revs:
+            notes.add(rev)
+            c = repo[rev]
+            print "N inline :%u" % rev_to_mark(c)
+            msg = c.hex()
+            print "data %d" % (len(msg))
+            print msg
+        print
+
+        marks.last_note = note_mark
+
     marks.set_tip(ename, head.hex())
 
 def export_tag(repo, tag):
@@ -550,12 +571,9 @@
     export_ref(repo, branch, 'branches', head)
 
 def export_head(repo):
-    global g_head
     export_ref(repo, g_head[0], 'bookmarks', g_head[1])
 
 def do_capabilities(parser):
-    global prefix, dirname
-
     print "import"
     print "export"
     print "refspec refs/heads/branches/*:%s/branches/*" % prefix
@@ -575,8 +593,6 @@
     return branches[branch][-1]
 
 def get_branch_tip(repo, branch):
-    global branches
-
     heads = branches.get(hgref(branch), None)
     if not heads:
         return None
@@ -589,7 +605,7 @@
     return heads[0]
 
 def list_head(repo, cur):
-    global g_head, bmarks, fake_bmark
+    global g_head, fake_bmark
 
     if 'default' not in branches:
         # empty repo
@@ -605,8 +621,6 @@
     g_head = (head, node)
 
 def do_list(parser):
-    global branches, bmarks, track_branches
-
     repo = parser.repo
     for bmark, node in bookmarks.listbookmarks(repo).iteritems():
         bmarks[bmark] = repo[node]
@@ -674,8 +688,6 @@
     print 'done'
 
 def parse_blob(parser):
-    global blob_marks
-
     parser.next()
     mark = parser.get_mark()
     parser.next()
@@ -692,9 +704,6 @@
             files[e] = f
 
 def parse_commit(parser):
-    global marks, blob_marks, parsed_refs
-    global mode
-
     from_mark = merge_mark = None
 
     ref = parser[1]
@@ -812,8 +821,6 @@
     marks.new_mark(node, commit_mark)
 
 def parse_reset(parser):
-    global parsed_refs
-
     ref = parser[1]
     parser.next()
     # ugh
@@ -1006,8 +1013,6 @@
         return tip in heads
 
 def do_export(parser):
-    global parsed_refs, bmarks, peer
-
     p_bmarks = []
     p_revs = {}
 
@@ -1079,7 +1084,7 @@
             author, msg = parsed_tags.get(tag, (None, None))
             if mode == 'git':
                 if not msg:
-                    msg = 'Added tag %s for changeset %s' % (tag, node[:12]);
+                    msg = 'Added tag %s for changeset %s' % (tag, node[:12])
                 tagnode, branch = write_tag(parser.repo, tag, node, msg, author)
                 p_revs[tagnode] = 'refs/heads/branches/' + gitref(branch)
             else:
@@ -1152,6 +1157,7 @@
     global filenodes
     global fake_bmark, hg_version
     global dry_run
+    global notes, alias
 
     alias = args[1]
     url = args[2]
@@ -1191,6 +1197,7 @@
     except:
         hg_version = None
     dry_run = False
+    notes = set()
 
     repo = get_repo(url, alias)
     prefix = 'refs/hg/%s' % alias
diff --git a/contrib/remote-helpers/test-bzr.sh b/contrib/remote-helpers/test-bzr.sh
index dce281f..5c50251 100755
--- a/contrib/remote-helpers/test-bzr.sh
+++ b/contrib/remote-helpers/test-bzr.sh
@@ -7,19 +7,21 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PYTHON; then
+if ! test_have_prereq PYTHON
+then
 	skip_all='skipping remote-bzr tests; python not available'
 	test_done
 fi
 
-if ! python -c 'import bzrlib'; then
+if ! python -c 'import bzrlib'
+then
 	skip_all='skipping remote-bzr tests; bzr not available'
 	test_done
 fi
 
 check () {
-	echo $3 > expected &&
-	git --git-dir=$1/.git log --format='%s' -1 $2 > actual
+	echo $3 >expected &&
+	git --git-dir=$1/.git log --format='%s' -1 $2 >actual
 	test_cmp expected actual
 }
 
@@ -29,7 +31,7 @@
 	(
 	bzr init bzrrepo &&
 	cd bzrrepo &&
-	echo one > content &&
+	echo one >content &&
 	bzr add content &&
 	bzr commit -m one
 	) &&
@@ -41,7 +43,7 @@
 test_expect_success 'pulling' '
 	(
 	cd bzrrepo &&
-	echo two > content &&
+	echo two >content &&
 	bzr commit -m two
 	) &&
 
@@ -53,13 +55,13 @@
 test_expect_success 'pushing' '
 	(
 	cd gitrepo &&
-	echo three > content &&
+	echo three >content &&
 	git commit -a -m three &&
 	git push
 	) &&
 
-	echo three > expected &&
-	cat bzrrepo/content > actual &&
+	echo three >expected &&
+	cat bzrrepo/content >actual &&
 	test_cmp expected actual
 '
 
@@ -67,16 +69,16 @@
 	(
 	cd gitrepo &&
 	git pull &&
-	git log --format="%s" -1 origin/master > actual
+	git log --format="%s" -1 origin/master >actual
 	) &&
-	echo three > expected &&
+	echo three >expected &&
 	test_cmp expected actual &&
 
 	(cd gitrepo && git push && git pull) &&
 
 	(
 	cd bzrrepo &&
-	echo four > content &&
+	echo four >content &&
 	bzr commit -m four
 	) &&
 
@@ -86,19 +88,19 @@
 
 	(
 	cd gitrepo &&
-	echo five > content &&
+	echo five >content &&
 	git commit -a -m five &&
 	git push && git pull
 	) &&
 
 	(cd bzrrepo && bzr revert) &&
 
-	echo five > expected &&
-	cat bzrrepo/content > actual &&
+	echo five >expected &&
+	cat bzrrepo/content >actual &&
 	test_cmp expected actual
 '
 
-cat > expected <<EOF
+cat >expected <<\EOF
 100644 blob 54f9d6da5c91d556e6b54340b1327573073030af	content
 100755 blob 68769579c3eaadbe555379b9c3538e6628bae1eb	executable
 120000 blob 6b584e8ece562ebffc15d38808cd6b98fc3d97ea	link
@@ -107,7 +109,7 @@
 test_expect_success 'special modes' '
 	(
 	cd bzrrepo &&
-	echo exec > executable
+	echo exec >executable
 	chmod +x executable &&
 	bzr add executable
 	bzr commit -m exec &&
@@ -122,21 +124,21 @@
 	(
 	cd gitrepo &&
 	git pull
-	git ls-tree HEAD > ../actual
+	git ls-tree HEAD >../actual
 	) &&
 
 	test_cmp expected actual &&
 
 	(
 	cd gitrepo &&
-	git cat-file -p HEAD:link > ../actual
+	git cat-file -p HEAD:link >../actual
 	) &&
 
-	printf content > expected &&
+	printf content >expected &&
 	test_cmp expected actual
 '
 
-cat > expected <<EOF
+cat >expected <<\EOF
 100644 blob 54f9d6da5c91d556e6b54340b1327573073030af	content
 100755 blob 68769579c3eaadbe555379b9c3538e6628bae1eb	executable
 120000 blob 6b584e8ece562ebffc15d38808cd6b98fc3d97ea	link
@@ -147,8 +149,8 @@
 	(
 	cd bzrrepo &&
 	mkdir movedir &&
-	echo one > movedir/one &&
-	echo two > movedir/two &&
+	echo one >movedir/one &&
+	echo two >movedir/two &&
 	bzr add movedir &&
 	bzr commit -m movedir &&
 	bzr mv movedir movedir-new &&
@@ -158,7 +160,7 @@
 	(
 	cd gitrepo &&
 	git pull &&
-	git ls-tree HEAD > ../actual
+	git ls-tree HEAD >../actual
 	) &&
 
 	test_cmp expected actual
@@ -167,7 +169,7 @@
 test_expect_success 'different authors' '
 	(
 	cd bzrrepo &&
-	echo john >> content &&
+	echo john >>content &&
 	bzr commit -m john \
 	  --author "Jane Rey <jrey@example.com>" \
 	  --author "John Doe <jdoe@example.com>"
@@ -176,10 +178,10 @@
 	(
 	cd gitrepo &&
 	git pull &&
-	git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual
+	git show --format="%an <%ae>, %cn <%ce>" --quiet >../actual
 	) &&
 
-	echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
+	echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" >expected &&
 	test_cmp expected actual
 '
 
@@ -196,12 +198,12 @@
 	bzr init bzrrepo &&
 	cd bzrrepo &&
 
-	echo test >> "ærø" &&
+	echo test >>"ærø" &&
 	bzr add "ærø" &&
-	echo test >> "ø~?" &&
+	echo test >>"ø~?" &&
 	bzr add "ø~?" &&
 	bzr commit -m add-utf-8 &&
-	echo test >> "ærø" &&
+	echo test >>"ærø" &&
 	bzr commit -m test-utf-8 &&
 	bzr rm "ø~?" &&
 	bzr mv "ærø" "ø~?" &&
@@ -211,9 +213,9 @@
 	(
 	git clone "bzr::bzrrepo" gitrepo &&
 	cd gitrepo &&
-	git -c core.quotepath=false ls-files > ../actual
+	git -c core.quotepath=false ls-files >../actual
 	) &&
-	echo "ø~?" > expected &&
+	echo "ø~?" >expected &&
 	test_cmp expected actual
 '
 
@@ -229,7 +231,7 @@
 	bzr init bzrrepo &&
 	cd bzrrepo &&
 
-	echo one >> content &&
+	echo one >>content &&
 	bzr add content &&
 	bzr commit -m one
 	) &&
@@ -238,15 +240,15 @@
 	git clone "bzr::bzrrepo" gitrepo &&
 	cd gitrepo &&
 
-	echo test >> "ærø" &&
+	echo test >>"ærø" &&
 	git add "ærø" &&
 	git commit -m utf-8 &&
 
 	git push
 	) &&
 
-	(cd bzrrepo && bzr ls > ../actual) &&
-	printf "content\nærø\n" > expected &&
+	(cd bzrrepo && bzr ls >../actual) &&
+	printf "content\nærø\n" >expected &&
 	test_cmp expected actual
 '
 
@@ -256,7 +258,7 @@
 	(
 	bzr init bzrrepo &&
 	cd bzrrepo &&
-	echo one > content &&
+	echo one >content &&
 	bzr add content &&
 	bzr commit -m one
 	) &&
@@ -265,27 +267,27 @@
 
 	(
 	cd bzrrepo &&
-	echo two > content &&
+	echo two >content &&
 	bzr commit -m two
 	) &&
 
 	(
 	cd gitrepo &&
-	echo three > content &&
+	echo three >content &&
 	git commit -a -m three &&
 	git fetch &&
 	git merge origin/master || true &&
-	echo three > content &&
+	echo three >content &&
 	git commit -a --no-edit &&
 	git push
 	) &&
 
-	echo three > expected &&
-	cat bzrrepo/content > actual &&
+	echo three >expected &&
+	cat bzrrepo/content >actual &&
 	test_cmp expected actual
 '
 
-cat > expected <<EOF
+cat >expected <<\EOF
 origin/HEAD
 origin/branch
 origin/trunk
@@ -299,7 +301,7 @@
 	(
 	bzr init bzrrepo/trunk &&
 	cd bzrrepo/trunk &&
-	echo one >> content &&
+	echo one >>content &&
 	bzr add content &&
 	bzr commit -m one
 	) &&
@@ -307,14 +309,14 @@
 	(
 	bzr branch bzrrepo/trunk bzrrepo/branch &&
 	cd bzrrepo/branch &&
-	echo two >> content &&
+	echo two >>content &&
 	bzr commit -m one
 	) &&
 
 	(
 	git clone "bzr::bzrrepo" gitrepo &&
 	cd gitrepo &&
-	git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual
+	git for-each-ref --format "%(refname:short)" refs/remotes/origin >../actual
 	) &&
 
 	test_cmp expected actual
@@ -327,11 +329,11 @@
 	bzr init bzrrepo &&
 	cd bzrrepo &&
 
-	echo one >> content &&
+	echo one >>content &&
 	bzr add content &&
 	bzr commit -m one &&
 
-	echo two >> content &&
+	echo two >>content &&
 	bzr commit -m two
 	) &&
 
@@ -341,21 +343,51 @@
 	cd bzrrepo &&
 	bzr uncommit --force &&
 
-	echo three >> content &&
+	echo three >>content &&
 	bzr commit -m three &&
 
-	echo four >> content &&
+	echo four >>content &&
 	bzr commit -m four &&
-	bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected
+	bzr log --line | sed -e "s/^[0-9][0-9]*: //" >../expected
 	) &&
 
 	(
 	cd gitrepo &&
 	git fetch &&
-	git log --format="%an %ad %s" --date=short origin/master > ../actual
+	git log --format="%an %ad %s" --date=short origin/master >../actual
 	) &&
 
 	test_cmp expected actual
 '
 
+test_expect_success 'export utf-8 authors' '
+	test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C && unset GIT_COMMITTER_NAME" &&
+
+	LC_ALL=en_US.UTF-8
+	export LC_ALL
+
+	GIT_COMMITTER_NAME="Grégoire"
+	export GIT_COMMITTER_NAME
+
+	bzr init bzrrepo &&
+
+	(
+	git init gitrepo &&
+	cd gitrepo &&
+	echo greg >>content &&
+	git add content &&
+	git commit -m one &&
+	git remote add bzr "bzr::../bzrrepo" &&
+	git push bzr
+	) &&
+
+	(
+	cd bzrrepo &&
+	bzr log | grep "^committer: " >../actual
+	) &&
+
+	echo "committer: Grégoire <committer@example.com>" >expected &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh
index f83d67d..e24c51d 100755
--- a/contrib/remote-helpers/test-hg-bidi.sh
+++ b/contrib/remote-helpers/test-hg-bidi.sh
@@ -10,12 +10,14 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PYTHON; then
+if ! test_have_prereq PYTHON
+then
 	skip_all='skipping remote-hg tests; python not available'
 	test_done
 fi
 
-if ! python -c 'import mercurial'; then
+if ! python -c 'import mercurial'
+then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
@@ -43,7 +45,7 @@
 	git checkout -q -b tmp &&
 	git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
 	git checkout -q @{-1} &&
-	git branch -q -D tmp 2> /dev/null || true
+	git branch -q -D tmp 2>/dev/null || true
 	)
 }
 
@@ -62,7 +64,7 @@
 	echo "tag = -d \"0 0\""
 	echo "[extensions]"
 	echo "graphlog ="
-	) >> "$HOME"/.hgrc &&
+	) >>"$HOME"/.hgrc &&
 	git config --global remote-hg.hg-git-compat true
 	git config --global remote-hg.track-branches true
 
@@ -81,22 +83,22 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add älphà" &&
 
 	GIT_AUTHOR_NAME="tést èncödîng" &&
 	export GIT_AUTHOR_NAME &&
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta" &&
 
-	echo gamma > gamma &&
+	echo gamma >gamma &&
 	git add gamma &&
 	git commit -m "add gämmâ" &&
 
 	: TODO git config i18n.commitencoding latin-1 &&
-	echo delta > delta &&
+	echo delta >delta &&
 	git add delta &&
 	git commit -m "add déltà"
 	) &&
@@ -105,8 +107,8 @@
 	git_clone hgrepo gitrepo2 &&
 	hg_clone gitrepo2 hgrepo2 &&
 
-	HGENCODING=utf-8 hg_log hgrepo > expected &&
-	HGENCODING=utf-8 hg_log hgrepo2 > actual &&
+	HGENCODING=utf-8 hg_log hgrepo >expected &&
+	HGENCODING=utf-8 hg_log hgrepo2 >actual &&
 
 	test_cmp expected actual
 '
@@ -117,14 +119,14 @@
 	(
 	git init -q gitrepo &&
 	cd gitrepo &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta"
 	mkdir foo &&
-	echo blah > foo/bar &&
+	echo blah >foo/bar &&
 	git add foo &&
 	git commit -m "add foo" &&
 	git rm alpha &&
@@ -137,8 +139,8 @@
 	git_clone hgrepo gitrepo2 &&
 	hg_clone gitrepo2 hgrepo2 &&
 
-	hg_log hgrepo > expected &&
-	hg_log hgrepo2 > actual &&
+	hg_log hgrepo >expected &&
+	hg_log hgrepo2 >actual &&
 
 	test_cmp expected actual
 '
@@ -150,12 +152,12 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 	git config receive.denyCurrentBranch ignore &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
 	git tag alpha &&
 
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta" &&
 	git tag -a -m "added tag beta" beta
@@ -165,8 +167,8 @@
 	git_clone hgrepo gitrepo2 &&
 	hg_clone gitrepo2 hgrepo2 &&
 
-	hg_log hgrepo > expected &&
-	hg_log hgrepo2 > actual &&
+	hg_log hgrepo >expected &&
+	hg_log hgrepo2 >actual &&
 
 	test_cmp expected actual
 '
@@ -178,7 +180,7 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -q -m "add alpha" &&
 	git checkout -q -b not-master
@@ -201,8 +203,8 @@
 	: Back to the common revision &&
 	(cd hgrepo && hg checkout default) &&
 
-	hg_log hgrepo > expected &&
-	hg_log hgrepo2 > actual &&
+	hg_log hgrepo >expected &&
+	hg_log hgrepo2 >actual &&
 
 	test_cmp expected actual
 '
@@ -214,7 +216,7 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
 	git checkout -q -b not-master
@@ -231,8 +233,8 @@
 	hg_push hgrepo gitrepo &&
 	hg_clone gitrepo hgrepo2 &&
 
-	hg_log hgrepo > expected &&
-	hg_log hgrepo2 > actual &&
+	hg_log hgrepo >expected &&
+	hg_log hgrepo2 >actual &&
 
 	test_cmp expected actual
 '
diff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh
index 2219284..6dcd95d 100755
--- a/contrib/remote-helpers/test-hg-hg-git.sh
+++ b/contrib/remote-helpers/test-hg-hg-git.sh
@@ -10,17 +10,20 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PYTHON; then
+if ! test_have_prereq PYTHON
+then
 	skip_all='skipping remote-hg tests; python not available'
 	test_done
 fi
 
-if ! python -c 'import mercurial'; then
+if ! python -c 'import mercurial'
+then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
 
-if ! python -c 'import hggit'; then
+if ! python -c 'import hggit'
+then
 	skip_all='skipping remote-hg tests; hg-git not available'
 	test_done
 fi
@@ -66,7 +69,7 @@
 	git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
 	git branch -D default &&
 	git checkout -q @{-1} &&
-	git branch -q -D tmp 2> /dev/null || true
+	git branch -q -D tmp 2>/dev/null || true
 	)
 }
 
@@ -100,7 +103,7 @@
 	echo "hgext.bookmarks ="
 	echo "hggit ="
 	echo "graphlog ="
-	) >> "$HOME"/.hgrc &&
+	) >>"$HOME"/.hgrc &&
 	git config --global receive.denycurrentbranch warn
 	git config --global remote-hg.hg-git-compat true
 	git config --global remote-hg.track-branches false
@@ -121,7 +124,7 @@
 	(
 	git init -q gitrepo &&
 	cd gitrepo &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	chmod 0644 alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
@@ -133,17 +136,18 @@
 	git commit -m "clear executable bit"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		hg_clone_$x gitrepo hgrepo-$x &&
 		cd hgrepo-$x &&
 		hg_log . &&
 		hg manifest -r 1 -v &&
 		hg manifest -v
-		) > output-$x &&
+		) >"output-$x" &&
 
 		git_clone_$x hgrepo-$x gitrepo2-$x &&
-		git_log gitrepo2-$x > log-$x
+		git_log gitrepo2-$x >"log-$x"
 	done &&
 
 	test_cmp output-hg output-git &&
@@ -156,7 +160,7 @@
 	(
 	git init -q gitrepo &&
 	cd gitrepo &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
 	ln -s alpha beta &&
@@ -164,16 +168,17 @@
 	git commit -m "add beta"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		hg_clone_$x gitrepo hgrepo-$x &&
 		cd hgrepo-$x &&
 		hg_log . &&
 		hg manifest -v
-		) > output-$x &&
+		) >"output-$x" &&
 
 		git_clone_$x hgrepo-$x gitrepo2-$x &&
-		git_log gitrepo2-$x > log-$x
+		git_log gitrepo2-$x >"log-$x"
 	done &&
 
 	test_cmp output-hg output-git &&
@@ -186,28 +191,29 @@
 	(
 	hg init hgrepo1 &&
 	cd hgrepo1 &&
-	echo A > afile &&
+	echo A >afile &&
 	hg add afile &&
 	hg ci -m "origin" &&
 
-	echo B > afile &&
+	echo B >afile &&
 	hg ci -m "A->B" &&
 
 	hg up -r0 &&
-	echo C > afile &&
+	echo C >afile &&
 	hg ci -m "A->C" &&
 
 	hg merge -r1 &&
-	echo C > afile &&
+	echo C >afile &&
 	hg resolve -m afile &&
 	hg ci -m "merge to C"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		git_clone_$x hgrepo1 gitrepo-$x &&
 		hg_clone_$x gitrepo-$x hgrepo2-$x &&
-		hg_log hgrepo2-$x > hg-log-$x &&
-		git_log gitrepo-$x > git-log-$x
+		hg_log hgrepo2-$x >"hg-log-$x" &&
+		git_log gitrepo-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -220,28 +226,29 @@
 	(
 	hg init hgrepo1 &&
 	cd hgrepo1 &&
-	echo A > afile &&
+	echo A >afile &&
 	hg add afile &&
 	hg ci -m "origin" &&
 
-	echo B > afile &&
+	echo B >afile &&
 	hg ci -m "A->B" &&
 
 	hg up -r0 &&
-	echo C > afile &&
+	echo C >afile &&
 	hg ci -m "A->C" &&
 
 	hg merge -r1 || true &&
-	echo B > afile &&
+	echo B >afile &&
 	hg resolve -m afile &&
 	hg ci -m "merge to B"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		git_clone_$x hgrepo1 gitrepo-$x &&
 		hg_clone_$x gitrepo-$x hgrepo2-$x &&
-		hg_log hgrepo2-$x > hg-log-$x &&
-		git_log gitrepo-$x > git-log-$x
+		hg_log hgrepo2-$x >"hg-log-$x" &&
+		git_log gitrepo-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -254,29 +261,30 @@
 	(
 	hg init hgrepo1 &&
 	cd hgrepo1 &&
-	echo A > afile &&
+	echo A >afile &&
 	hg add afile &&
 	hg ci -m "origin" &&
 
-	echo B > afile &&
+	echo B >afile &&
 	hg ci -m "A->B" &&
 
-	echo C > afile &&
+	echo C >afile &&
 	hg ci -m "B->C" &&
 
 	hg up -r0 &&
-	echo C > afile &&
+	echo C >afile &&
 	hg ci -m "A->C" &&
 
 	hg merge -r2 || true &&
 	hg ci -m "merge"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		git_clone_$x hgrepo1 gitrepo-$x &&
 		hg_clone_$x gitrepo-$x hgrepo2-$x &&
-		hg_log hgrepo2-$x > hg-log-$x &&
-		git_log gitrepo-$x > git-log-$x
+		hg_log hgrepo2-$x >"hg-log-$x" &&
+		git_log gitrepo-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -290,32 +298,33 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add älphà" &&
 
 	GIT_AUTHOR_NAME="tést èncödîng" &&
 	export GIT_AUTHOR_NAME &&
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta" &&
 
-	echo gamma > gamma &&
+	echo gamma >gamma &&
 	git add gamma &&
 	git commit -m "add gämmâ" &&
 
 	: TODO git config i18n.commitencoding latin-1 &&
-	echo delta > delta &&
+	echo delta >delta &&
 	git add delta &&
 	git commit -m "add déltà"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		hg_clone_$x gitrepo hgrepo-$x &&
 		git_clone_$x hgrepo-$x gitrepo2-$x &&
 
-		HGENCODING=utf-8 hg_log hgrepo-$x > hg-log-$x &&
-		git_log gitrepo2-$x > git-log-$x
+		HGENCODING=utf-8 hg_log hgrepo-$x >"hg-log-$x" &&
+		git_log gitrepo2-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -328,14 +337,14 @@
 	(
 	git init -q gitrepo &&
 	cd gitrepo &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta"
 	mkdir foo &&
-	echo blah > foo/bar &&
+	echo blah >foo/bar &&
 	git add foo &&
 	git commit -m "add foo" &&
 	git rm alpha &&
@@ -344,17 +353,18 @@
 	git commit -m "remove foo/bar"
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		hg_clone_$x gitrepo hgrepo-$x &&
 		cd hgrepo-$x &&
 		hg_log . &&
 		hg manifest -r 3 &&
 		hg manifest
-		) > output-$x &&
+		) >"output-$x" &&
 
 		git_clone_$x hgrepo-$x gitrepo2-$x &&
-		git_log gitrepo2-$x > log-$x
+		git_log gitrepo2-$x >"log-$x"
 	done &&
 
 	test_cmp output-hg output-git &&
@@ -368,20 +378,21 @@
 	git init -q gitrepo &&
 	cd gitrepo &&
 	git config receive.denyCurrentBranch ignore &&
-	echo alpha > alpha &&
+	echo alpha >alpha &&
 	git add alpha &&
 	git commit -m "add alpha" &&
 	git tag alpha &&
 
-	echo beta > beta &&
+	echo beta >beta &&
 	git add beta &&
 	git commit -m "add beta" &&
 	git tag -a -m "added tag beta" beta
 	) &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		hg_clone_$x gitrepo hgrepo-$x &&
-		hg_log hgrepo-$x > log-$x
+		hg_log hgrepo-$x >"log-$x"
 	done &&
 
 	test_cmp log-hg log-git
@@ -390,12 +401,13 @@
 test_expect_success 'hg author' '
 	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		git init -q gitrepo-$x &&
 		cd gitrepo-$x &&
 
-		echo alpha > alpha &&
+		echo alpha >alpha &&
 		git add alpha &&
 		git commit -m "add alpha" &&
 		git checkout -q -b not-master
@@ -406,38 +418,38 @@
 		cd hgrepo-$x &&
 
 		hg co master &&
-		echo beta > beta &&
+		echo beta >beta &&
 		hg add beta &&
 		hg commit -u "test" -m "add beta" &&
 
-		echo gamma >> beta &&
+		echo gamma >>beta &&
 		hg commit -u "test <test@example.com> (comment)" -m "modify beta" &&
 
-		echo gamma > gamma &&
+		echo gamma >gamma &&
 		hg add gamma &&
 		hg commit -u "<test@example.com>" -m "add gamma" &&
 
-		echo delta > delta &&
+		echo delta >delta &&
 		hg add delta &&
 		hg commit -u "name<test@example.com>" -m "add delta" &&
 
-		echo epsilon > epsilon &&
+		echo epsilon >epsilon &&
 		hg add epsilon &&
 		hg commit -u "name <test@example.com" -m "add epsilon" &&
 
-		echo zeta > zeta &&
+		echo zeta >zeta &&
 		hg add zeta &&
 		hg commit -u " test " -m "add zeta" &&
 
-		echo eta > eta &&
+		echo eta >eta &&
 		hg add eta &&
 		hg commit -u "test < test@example.com >" -m "add eta" &&
 
-		echo theta > theta &&
+		echo theta >theta &&
 		hg add theta &&
 		hg commit -u "test >test@example.com>" -m "add theta" &&
 
-		echo iota > iota &&
+		echo iota >iota &&
 		hg add iota &&
 		hg commit -u "test <test <at> example <dot> com>" -m "add iota"
 		) &&
@@ -445,8 +457,8 @@
 		hg_push_$x hgrepo-$x gitrepo-$x &&
 		hg_clone_$x gitrepo-$x hgrepo2-$x &&
 
-		hg_log hgrepo2-$x > hg-log-$x &&
-		git_log gitrepo-$x > git-log-$x
+		hg_log hgrepo2-$x >"hg-log-$x" &&
+		git_log gitrepo-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -456,12 +468,13 @@
 test_expect_success 'hg branch' '
 	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		git init -q gitrepo-$x &&
 		cd gitrepo-$x &&
 
-		echo alpha > alpha &&
+		echo alpha >alpha &&
 		git add alpha &&
 		git commit -q -m "add alpha" &&
 		git checkout -q -b not-master
@@ -481,8 +494,8 @@
 		hg_push_$x hgrepo-$x gitrepo-$x &&
 		hg_clone_$x gitrepo-$x hgrepo2-$x &&
 
-		hg_log hgrepo2-$x > hg-log-$x &&
-		git_log gitrepo-$x > git-log-$x
+		hg_log hgrepo2-$x >"hg-log-$x" &&
+		git_log gitrepo-$x >"git-log-$x"
 	done &&
 
 	test_cmp hg-log-hg hg-log-git &&
@@ -492,12 +505,13 @@
 test_expect_success 'hg tags' '
 	test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
-	for x in hg git; do
+	for x in hg git
+	do
 		(
 		git init -q gitrepo-$x &&
 		cd gitrepo-$x &&
 
-		echo alpha > alpha &&
+		echo alpha >alpha &&
 		git add alpha &&
 		git commit -m "add alpha" &&
 		git checkout -q -b not-master
@@ -518,7 +532,7 @@
 		git --git-dir=gitrepo-$x/.git tag -l &&
 		hg_log hgrepo2-$x &&
 		cat hgrepo2-$x/.hgtags
-		) > output-$x
+		) >"output-$x"
 	done &&
 
 	test_cmp output-hg output-git
diff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh
index f7ce8aa..72f745d 100755
--- a/contrib/remote-helpers/test-hg.sh
+++ b/contrib/remote-helpers/test-hg.sh
@@ -10,40 +10,44 @@
 
 . ./test-lib.sh
 
-if ! test_have_prereq PYTHON; then
+if ! test_have_prereq PYTHON
+then
 	skip_all='skipping remote-hg tests; python not available'
 	test_done
 fi
 
-if ! python -c 'import mercurial'; then
+if ! python -c 'import mercurial'
+then
 	skip_all='skipping remote-hg tests; mercurial not available'
 	test_done
 fi
 
 check () {
-	echo $3 > expected &&
-	git --git-dir=$1/.git log --format='%s' -1 $2 > actual
+	echo $3 >expected &&
+	git --git-dir=$1/.git log --format='%s' -1 $2 >actual
 	test_cmp expected actual
 }
 
 check_branch () {
-	if [ -n "$3" ]; then
-		echo $3 > expected &&
-		hg -R $1 log -r $2 --template '{desc}\n' > actual &&
+	if test -n "$3"
+	then
+		echo $3 >expected &&
+		hg -R $1 log -r $2 --template '{desc}\n' >actual &&
 		test_cmp expected actual
 	else
-		hg -R $1 branches > out &&
+		hg -R $1 branches >out &&
 		! grep $2 out
 	fi
 }
 
 check_bookmark () {
-	if [ -n "$3" ]; then
-		echo $3 > expected &&
-		hg -R $1 log -r "bookmark('$2')" --template '{desc}\n' > actual &&
+	if test -n "$3"
+	then
+		echo $3 >expected &&
+		hg -R $1 log -r "bookmark('$2')" --template '{desc}\n' >actual &&
 		test_cmp expected actual
 	else
-		hg -R $1 bookmarks > out &&
+		hg -R $1 bookmarks >out &&
 		! grep $2 out
 	fi
 }
@@ -52,7 +56,7 @@
 	local expected_ret=$1 ret=0 ref_ret=0 IFS=':'
 
 	shift
-	git push origin "$@" 2> error
+	git push origin "$@" 2>error
 	ret=$?
 	cat error
 
@@ -75,10 +79,10 @@
 			grep "^   [a-f0-9]*\.\.[a-f0-9]* *${branch} -> ${branch}$" error || ref_ret=1
 			;;
 		esac
-		let 'ref_ret' && echo "match for '$branch' failed" && break
+		test $ref_ret -ne 0 && echo "match for '$branch' failed" && break
 	done
 
-	if let 'expected_ret != ret || ref_ret'
+	if test $expected_ret -ne $ret -o $ref_ret -ne 0
 	then
 		return 1
 	fi
@@ -92,7 +96,7 @@
 	echo "username = H G Wells <wells@example.com>"
 	echo "[extensions]"
 	echo "mq ="
-	) >> "$HOME"/.hgrc &&
+	) >>"$HOME"/.hgrc &&
 
 	GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" &&
 	GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" &&
@@ -107,7 +111,7 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero
 	) &&
@@ -122,7 +126,7 @@
 	(
 	cd hgrepo &&
 	hg branch next &&
-	echo next > content &&
+	echo next >content &&
 	hg commit -m next
 	) &&
 
@@ -137,7 +141,7 @@
 	cd hgrepo &&
 	hg checkout default &&
 	hg bookmark feature-a &&
-	echo feature-a > content &&
+	echo feature-a >content &&
 	hg commit -m feature-a
 	) &&
 
@@ -157,7 +161,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git checkout --quiet devel &&
-	echo devel > content &&
+	echo devel >content &&
 	git commit -a -m devel &&
 	git push --quiet
 	) &&
@@ -172,7 +176,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git checkout --quiet -b feature-b &&
-	echo feature-b > content &&
+	echo feature-b >content &&
 	git commit -a -m feature-b &&
 	git push --quiet origin feature-b
 	) &&
@@ -184,9 +188,9 @@
 rm -rf hgrepo
 
 author_test () {
-	echo $1 >> content &&
+	echo $1 >>content &&
 	hg commit -u "$2" -m "add $1" &&
-	echo "$3" >> ../expected
+	echo "$3" >>../expected
 }
 
 test_expect_success 'authors' '
@@ -199,7 +203,7 @@
 	touch content &&
 	hg add content &&
 
-	> ../expected &&
+	>../expected &&
 	author_test alpha "" "H G Wells <wells@example.com>" &&
 	author_test beta "test" "test <unknown>" &&
 	author_test beta "test <test@example.com> (comment)" "test <test@example.com>" &&
@@ -214,7 +218,7 @@
 	) &&
 
 	git clone "hg::hgrepo" gitrepo &&
-	git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual &&
+	git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" >actual &&
 
 	test_cmp expected actual
 '
@@ -226,11 +230,11 @@
 	hg init hgrepo &&
 	cd hgrepo &&
 
-	echo one >> content &&
+	echo one >>content &&
 	hg add content &&
 	hg commit -m one &&
 
-	echo two >> content &&
+	echo two >>content &&
 	hg commit -m two
 	) &&
 
@@ -240,20 +244,20 @@
 	cd hgrepo &&
 	hg strip 1 &&
 
-	echo three >> content &&
+	echo three >>content &&
 	hg commit -m three &&
 
-	echo four >> content &&
+	echo four >>content &&
 	hg commit -m four
 	) &&
 
 	(
 	cd gitrepo &&
 	git fetch &&
-	git log --format="%s" origin/master > ../actual
+	git log --format="%s" origin/master >../actual
 	) &&
 
-	hg -R hgrepo log --template "{desc}\n" > expected &&
+	hg -R hgrepo log --template "{desc}\n" >expected &&
 	test_cmp actual expected
 '
 
@@ -263,18 +267,18 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero &&
 	hg bookmark master &&
-	echo one > content &&
+	echo one >content &&
 	hg commit -m one
 	) &&
 
 	(
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
-	echo two > content &&
+	echo two >content &&
 	git commit -a -m two &&
 	git push
 	) &&
@@ -282,7 +286,7 @@
 	check_branch hgrepo default two
 '
 
-cat > expected <<EOF
+cat >expected <<\EOF
 changeset:   0:6e2126489d3d
 tag:         tip
 user:        A U Thor <author@example.com>
@@ -300,13 +304,13 @@
 	git init gitrepo &&
 	cd gitrepo &&
 	git remote add origin "hg::../hgrepo" &&
-	echo one > content &&
+	echo one >content &&
 	git add content &&
 	git commit -a -m one &&
 	git push origin master
 	) &&
 
-	hg -R hgrepo log > actual &&
+	hg -R hgrepo log >actual &&
 	cat actual &&
 	test_cmp expected actual &&
 
@@ -322,7 +326,7 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero
 	) &&
@@ -343,7 +347,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git checkout --quiet devel &&
-	echo devel > content &&
+	echo devel >content &&
 	git commit -a -m devel &&
 	git push --quiet
 	) &&
@@ -358,7 +362,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git checkout --quiet -b feature-b &&
-	echo feature-b > content &&
+	echo feature-b >content &&
 	git commit -a -m feature-b &&
 	git push --quiet origin feature-b
 	) &&
@@ -374,15 +378,15 @@
 	(
 	cd hgrepo &&
 	hg checkout default &&
-	echo bump > content &&
+	echo bump >content &&
 	hg commit -m bump
 	) &&
 
 	(
 	cd gitrepo &&
-	echo diverge > content &&
+	echo diverge >content &&
 	git commit -a -m diverged &&
-	check_push 1 <<-EOF
+	check_push 1 <<-\EOF
 	master:non-fast-forward
 	EOF
 	) &&
@@ -403,16 +407,16 @@
 
 	(
 	cd hgrepo &&
-	echo "bump bookmark" > content &&
+	echo "bump bookmark" >content &&
 	hg commit -m "bump bookmark"
 	) &&
 
 	(
 	cd gitrepo &&
 	git checkout --quiet diverge &&
-	echo diverge > content &&
+	echo diverge >content &&
 	git commit -a -m diverge &&
-	check_push 1 <<-EOF
+	check_push 1 <<-\EOF
 	diverge:fetch-first
 	EOF
 	) &&
@@ -427,7 +431,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git checkout --quiet -b feature-c HEAD^ &&
-	echo feature-c > content &&
+	echo feature-c >content &&
 	git commit -a -m feature-c &&
 	git push --quiet origin feature-c
 	) &&
@@ -442,20 +446,20 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero &&
 	hg bookmark bad_bmark1 &&
-	echo one > content &&
+	echo one >content &&
 	hg commit -m one &&
 	hg bookmark bad_bmark2 &&
 	hg bookmark good_bmark &&
 	hg bookmark -i good_bmark &&
 	hg -q branch good_branch &&
-	echo "good branch" > content &&
+	echo "good branch" >content &&
 	hg commit -m "good branch" &&
 	hg -q branch bad_branch &&
-	echo "bad branch" > content &&
+	echo "bad branch" >content &&
 	hg commit -m "bad branch"
 	) &&
 
@@ -463,40 +467,40 @@
 
 	(
 	cd gitrepo &&
-	echo two > content &&
+	echo two >content &&
 	git commit -q -a -m two &&
 
 	git checkout -q good_bmark &&
-	echo three > content &&
+	echo three >content &&
 	git commit -q -a -m three &&
 
 	git checkout -q bad_bmark1 &&
 	git reset --hard HEAD^ &&
-	echo four > content &&
+	echo four >content &&
 	git commit -q -a -m four &&
 
 	git checkout -q bad_bmark2 &&
 	git reset --hard HEAD^ &&
-	echo five > content &&
+	echo five >content &&
 	git commit -q -a -m five &&
 
 	git checkout -q -b new_bmark master &&
-	echo six > content &&
+	echo six >content &&
 	git commit -q -a -m six &&
 
 	git checkout -q branches/good_branch &&
-	echo seven > content &&
+	echo seven >content &&
 	git commit -q -a -m seven &&
-	echo eight > content &&
+	echo eight >content &&
 	git commit -q -a -m eight &&
 
 	git checkout -q branches/bad_branch &&
 	git reset --hard HEAD^ &&
-	echo nine > content &&
+	echo nine >content &&
 	git commit -q -a -m nine &&
 
 	git checkout -q -b branches/new_branch master &&
-	echo ten > content &&
+	echo ten >content &&
 	git commit -q -a -m ten
 	)
 }
@@ -509,7 +513,7 @@
 	(
 	cd gitrepo &&
 
-	check_push 1 --all <<-EOF
+	check_push 1 --all <<-\EOF
 	master
 	good_bmark
 	branches/good_branch
@@ -537,17 +541,17 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero &&
 	hg bookmark bad_bmark &&
 	hg bookmark good_bmark &&
 	hg bookmark -i good_bmark &&
 	hg -q branch good_branch &&
-	echo "good branch" > content &&
+	echo "good branch" >content &&
 	hg commit -m "good branch" &&
 	hg -q branch bad_branch &&
-	echo "bad branch" > content &&
+	echo "bad branch" >content &&
 	hg commit -m "bad branch"
 	) &&
 
@@ -556,39 +560,37 @@
 	(
 	cd hgrepo &&
 	hg bookmark -f bad_bmark &&
-	echo update_bmark > content &&
+	echo update_bmark >content &&
 	hg commit -m "update bmark"
 	) &&
 
 	(
 	cd gitrepo &&
-	echo two > content &&
+	echo two >content &&
 	git commit -q -a -m two &&
 
 	git checkout -q good_bmark &&
-	echo three > content &&
+	echo three >content &&
 	git commit -q -a -m three &&
 
 	git checkout -q bad_bmark &&
-	echo four > content &&
+	echo four >content &&
 	git commit -q -a -m four &&
 
 	git checkout -q branches/bad_branch &&
-	echo five > content &&
+	echo five >content &&
 	git commit -q -a -m five &&
 
-	check_push 1 --all <<-EOF
+	check_push 1 --all <<-\EOF &&
 	master
 	good_bmark
-	new_bmark:new
-	new_branch:new
 	bad_bmark:fetch-first
 	branches/bad_branch:festch-first
 	EOF
 
 	git fetch &&
 
-	check_push 1 --all <<-EOF
+	check_push 1 --all <<-\EOF
 	master
 	good_bmark
 	bad_bmark:non-fast-forward
@@ -605,7 +607,7 @@
 	(
 	cd gitrepo &&
 
-	check_push 0 --force --all <<-EOF
+	check_push 0 --force --all <<-\EOF
 	master
 	good_bmark
 	branches/good_branch
@@ -635,7 +637,7 @@
 	(
 	cd gitrepo &&
 
-	check_push 0 --dry-run --all <<-EOF
+	check_push 1 --dry-run --all <<-\EOF &&
 	master
 	good_bmark
 	branches/good_branch
@@ -646,7 +648,7 @@
 	branches/bad_branch:non-fast-forward
 	EOF
 
-	check_push 0 --dry-run master good_bmark new_bmark branches/good_branch branches/new_branch <<-EOF
+	check_push 0 --dry-run master good_bmark new_bmark branches/good_branch branches/new_branch <<-\EOF
 	master
 	good_bmark
 	branches/good_branch
@@ -671,10 +673,10 @@
 	(
 	hg init hgrepo &&
 	cd hgrepo &&
-	echo zero > content &&
+	echo zero >content &&
 	hg add content &&
 	hg commit -m zero &&
-	echo one > content &&
+	echo one >content &&
 	hg commit -m one
 	) &&
 
@@ -682,7 +684,7 @@
 	git clone "hg::hgrepo" gitrepo &&
 	cd gitrepo &&
 	git reset --hard HEAD^ &&
-	echo two > content &&
+	echo two >content &&
 	git commit -a -m two &&
 	test_expect_code 1 git push &&
 	test_expect_code 1 git push
diff --git a/diff-no-index.c b/diff-no-index.c
index e301aaf..00a8eef 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -215,9 +215,21 @@
 		     path_inside_repo(prefix, argv[i+1])))
 			return;
 	}
-	if (argc != i + 2)
+	if (argc != i + 2) {
+		if (!no_index) {
+			/*
+			 * There was no --no-index and there were not two
+			 * paths. It is possible that the user intended
+			 * to do an inside-repository operation.
+			 */
+			fprintf(stderr, "Not a git repository\n");
+			fprintf(stderr,
+				"To compare two paths outside a working tree:\n");
+		}
+		/* Give the usage message for non-repository usage and exit. */
 		usagef("git diff %s <path> <path>",
 		       no_index ? "--no-index" : "[--no-index]");
+	}
 
 	diff_setup(&revs->diffopt);
 	for (i = 1; i < argc - 2; ) {
diff --git a/dir.c b/dir.c
index 1128110..b439ff0 100644
--- a/dir.c
+++ b/dir.c
@@ -429,15 +429,14 @@
 	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);
+	pos = cache_name_pos(path, len);
 	if (pos < 0)
 		return NULL;
-	if (!ce_skip_worktree(istate->cache[pos]))
+	if (!ce_skip_worktree(active_cache[pos]))
 		return NULL;
-	data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+	data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
 	if (!data || type != OBJ_BLOB) {
 		free(data);
 		return NULL;
@@ -884,13 +883,13 @@
 };
 
 /*
- * Do not use the alphabetically stored index to look up
+ * Do not use the alphabetically sorted 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)
 {
-	const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+	const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case);
 	unsigned char endchar;
 
 	if (!ce)
@@ -1132,14 +1131,51 @@
 					  int dtype, struct dirent *de)
 {
 	int exclude;
+	int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case);
+
 	if (dtype == DT_UNKNOWN)
 		dtype = get_dtype(de, path->buf, path->len);
 
 	/* Always exclude indexed files */
-	if (dtype != DT_DIR &&
-	    cache_name_exists(path->buf, path->len, ignore_case))
+	if (dtype != DT_DIR && has_path_in_index)
 		return path_none;
 
+	/*
+	 * When we are looking at a directory P in the working tree,
+	 * there are three cases:
+	 *
+	 * (1) P exists in the index.  Everything inside the directory P in
+	 * the working tree needs to go when P is checked out from the
+	 * index.
+	 *
+	 * (2) P does not exist in the index, but there is P/Q in the index.
+	 * We know P will stay a directory when we check out the contents
+	 * of the index, but we do not know yet if there is a directory
+	 * P/Q in the working tree to be killed, so we need to recurse.
+	 *
+	 * (3) P does not exist in the index, and there is no P/Q in the index
+	 * to require P to be a directory, either.  Only in this case, we
+	 * know that everything inside P will not be killed without
+	 * recursing.
+	 */
+	if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
+	    (dtype == DT_DIR) &&
+	    !has_path_in_index) {
+		/*
+		 * NEEDSWORK: directory_exists_in_index_icase()
+		 * assumes that one byte past the given path is
+		 * readable and has '/', which needs to be fixed, but
+		 * until then, work it around in the caller.
+		 */
+		strbuf_addch(path, '/');
+		if (directory_exists_in_index(path->buf, path->len - 1) ==
+		    index_nonexistent) {
+			strbuf_setlen(path, path->len - 1);
+			return path_none;
+		}
+		strbuf_setlen(path, path->len - 1);
+	}
+
 	exclude = is_excluded(dir, path->buf, &dtype);
 
 	/*
diff --git a/dir.h b/dir.h
index 343ec7a..9b7e4e7 100644
--- a/dir.h
+++ b/dir.h
@@ -80,7 +80,8 @@
 		DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
 		DIR_NO_GITLINKS = 1<<3,
 		DIR_COLLECT_IGNORED = 1<<4,
-		DIR_SHOW_IGNORED_TOO = 1<<5
+		DIR_SHOW_IGNORED_TOO = 1<<5,
+		DIR_COLLECT_KILLED_ONLY = 1<<6
 	} flags;
 	struct dir_entry **entries;
 	struct dir_entry **ignored;
diff --git a/environment.c b/environment.c
index 5398c36..378254c 100644
--- a/environment.c
+++ b/environment.c
@@ -123,14 +123,13 @@
 
 static void setup_git_env(void)
 {
+	const char *gitfile;
+
 	git_dir = getenv(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;
+	gitfile = read_gitfile(git_dir);
+	git_dir = xstrdup(gitfile ? gitfile : git_dir);
 	git_object_dir = getenv(DB_ENVIRONMENT);
 	if (!git_object_dir) {
 		git_object_dir = xmalloc(strlen(git_dir) + 9);
diff --git a/fast-import.c b/fast-import.c
index 21db3fc..f4d9969 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -22,8 +22,8 @@
     ('author' (sp name)? sp '<' email '>' sp when lf)?
     'committer' (sp name)? sp '<' email '>' sp when lf
     commit_msg
-    ('from' sp committish lf)?
-    ('merge' sp committish lf)*
+    ('from' sp commit-ish lf)?
+    ('merge' sp commit-ish lf)*
     (file_change | ls)*
     lf?;
   commit_msg ::= data;
@@ -43,18 +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
+  note_obm ::= 'N' sp (hexsha1 | idnum) sp commit-ish lf;
+  note_inm ::= 'N' sp 'inline' sp commit-ish lf
     data;
 
   new_tag ::= 'tag' sp tag_str lf
-    'from' sp committish lf
+    'from' sp commit-ish lf
     ('tagger' (sp name)? sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
   reset_branch ::= 'reset' sp ref_str lf
-    ('from' sp committish lf)?
+    ('from' sp commit-ish lf)?
     lf?;
 
   checkpoint ::= 'checkpoint' lf
@@ -93,7 +93,7 @@
      # stream formatting is: \, " and LF.  Otherwise these values
      # are UTF8.
      #
-  committish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
+  commit-ish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
   ref_str     ::= ref;
   sha1exp_str ::= sha1exp;
   tag_str     ::= tag;
@@ -1694,7 +1694,7 @@
 		return 0;
 	if (read_ref(b->name, old_sha1))
 		hashclr(old_sha1);
-	lock = lock_any_ref_for_update(b->name, old_sha1, 0);
+	lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
 	if (!lock)
 		return error("Unable to lock %s", b->name);
 	if (!force_update && !is_null_sha1(old_sha1)) {
@@ -2494,7 +2494,7 @@
 	assert(*p == ' ');
 	p++;  /* skip space */
 
-	/* <committish> */
+	/* <commit-ish> */
 	s = lookup_branch(p);
 	if (s) {
 		if (is_null_sha1(s->sha1))
@@ -2973,7 +2973,7 @@
 	case OBJ_TAG:
 		break;
 	default:
-		die("Not a treeish: %s", command_buf.buf);
+		die("Not a tree-ish: %s", command_buf.buf);
 	}
 
 	if (oe->pack_id != MAX_PACK_ID) {	/* in a pack being written */
@@ -3057,7 +3057,7 @@
 	struct tree_entry *root = NULL;
 	struct tree_entry leaf = {NULL};
 
-	/* ls SP (<treeish> SP)? <path> */
+	/* ls SP (<tree-ish> SP)? <path> */
 	p = command_buf.buf + strlen("ls ");
 	if (*p == '"') {
 		if (!b)
diff --git a/fetch-pack.c b/fetch-pack.c
index 094267f..a0e0350 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -185,36 +185,6 @@
 	}
 }
 
-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)
 {
 	int len;
@@ -689,7 +659,7 @@
 	const char *argv[22];
 	char keep_arg[256];
 	char hdr_arg[256];
-	const char **av;
+	const char **av, *cmd_name;
 	int do_keep = args->keep_pack;
 	struct child_process cmd;
 	int ret;
@@ -736,7 +706,7 @@
 	if (do_keep) {
 		if (pack_lockfile)
 			cmd.out = -1;
-		*av++ = "index-pack";
+		*av++ = cmd_name = "index-pack";
 		*av++ = "--stdin";
 		if (!args->quiet && !args->no_progress)
 			*av++ = "-v";
@@ -753,7 +723,7 @@
 			*av++ = "--check-self-contained-and-connected";
 	}
 	else {
-		*av++ = "unpack-objects";
+		*av++ = cmd_name = "unpack-objects";
 		if (args->quiet || args->no_progress)
 			*av++ = "-q";
 		args->check_self_contained_and_connected = 0;
@@ -771,7 +741,7 @@
 	cmd.in = demux.out;
 	cmd.git_cmd = 1;
 	if (start_command(&cmd))
-		die("fetch-pack: unable to fork off %s", argv[0]);
+		die("fetch-pack: unable to fork off %s", cmd_name);
 	if (do_keep && pack_lockfile) {
 		*pack_lockfile = index_pack_lockfile(cmd.out);
 		close(cmd.out);
@@ -783,7 +753,7 @@
 			args->check_self_contained_and_connected &&
 			ret == 0;
 	else
-		die("%s failed", argv[0]);
+		die("%s failed", cmd_name);
 	if (use_sideband && finish_async(&demux))
 		die("error in sideband demultiplexer");
 	return 0;
@@ -796,27 +766,6 @@
 	return strcmp(a->name, b->name);
 }
 
-static void setup_alternate_shallow(void)
-{
-	struct strbuf sb = STRBUF_INIT;
-	int fd;
-
-	check_shallow_file_for_update();
-	fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
-				       LOCK_DIE_ON_ERROR);
-	if (write_shallow_commits(&sb, 0)) {
-		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
-			die_errno("failed to write to %s", shallow_lock.filename);
-		alternate_shallow_file = shallow_lock.filename;
-	} else
-		/*
-		 * is_repository_shallow() sees empty string as "no
-		 * shallow file".
-		 */
-		alternate_shallow_file = "";
-	strbuf_release(&sb);
-}
-
 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 int fd[2],
 				 const struct ref *orig_ref,
@@ -897,7 +846,7 @@
 	if (args->stateless_rpc)
 		packet_flush(fd[1]);
 	if (args->depth > 0)
-		setup_alternate_shallow();
+		setup_alternate_shallow(&shallow_lock, &alternate_shallow_file);
 	else
 		alternate_shallow_file = NULL;
 	if (get_pack(args, fd, pack_lockfile))
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 75a991f..5156384 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -169,7 +169,7 @@
 my %patch_mode_flavour = %{$patch_modes{stage}};
 
 sub run_cmd_pipe {
-	if ($^O eq 'MSWin32' || $^O eq 'msys') {
+	if ($^O eq 'MSWin32') {
 		my @invalid = grep {m/[":*]/} @_;
 		die "$^O does not support: @invalid\n" if @invalid;
 		my @args = map { m/ /o ? "\"$_\"": $_ } @_;
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index a0d796e..74d1cc7 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -4167,7 +4167,7 @@
     #  this half-converted form, but it isn't currently worth the
     #  backwards compatibility headaches.
 
-    $mode=~/^\d\d(\d)\d{3}$/;
+    $mode=~/^\d{3}(\d)\d\d$/;
     my $userBits=$1;
 
     my $dbMode = "";
@@ -4338,7 +4338,7 @@
 =head2 getRevisionDirMap
 
 A "revision dir map" contains all the plain-file filenames associated
-with a particular revision (treeish), organized by directory:
+with a particular revision (tree-ish), organized by directory:
 
   $type = $out->{$dir}{$fullName}
 
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index ac2a005..98e8fe4 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -283,11 +283,12 @@
 
 	case "$filter_subdir" in
 	"")
-		git read-tree -i -m $commit
+		GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
 		;;
 	*)
 		# The commit may not have the subdirectory at all
-		err=$(git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
+		err=$(GIT_ALLOW_NULL_SHA1=1 \
+		      git read-tree -i -m $commit:"$filter_subdir" 2>&1) || {
 			if ! git rev-parse -q --verify $commit:"$filter_subdir"
 			then
 				rm -f "$GIT_INDEX_FILE"
diff --git a/git-p4.py b/git-p4.py
index a53a6dc..06a3cc6 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -780,11 +780,14 @@
     # dictionary of all client parameters
     entry = specList[0]
 
+    # the //client/ name
+    client_name = entry["Client"]
+
     # 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()
+    view = View(client_name)
 
     # append the lines, in order, to the view
     for view_num in range(len(view_keys)):
@@ -1555,8 +1558,8 @@
             for b in body:
                 labelTemplate += "\t" + b + "\n"
             labelTemplate += "View:\n"
-            for mapping in clientSpec.mappings:
-                labelTemplate += "\t%s\n" % mapping.depot_side.path
+            for depot_side in clientSpec.mappings:
+                labelTemplate += "\t%s\n" % depot_side
 
             if self.dry_run:
                 print "Would create p4 label %s for tag" % name
@@ -1568,7 +1571,7 @@
 
                 # Use the label
                 p4_system(["tag", "-l", name] +
-                          ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
+                          ["%s@%s" % (depot_side, changelist) for depot_side in clientSpec.mappings])
 
                 if verbose:
                     print "created p4 label for tag %s" % name
@@ -1796,117 +1799,16 @@
     """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):
+    def __init__(self, client_name):
         self.mappings = []
+        self.client_prefix = "//%s/" % client_name
+        # cache results of "p4 where" to lookup client file locations
+        self.client_spec_path_cache = {}
 
     def append(self, view_line):
         """Parse a view line, splitting it into depot and client
-           sides.  Append to self.mappings, preserving order."""
+           sides.  Append to self.mappings, preserving order.  This
+           is only needed for tag creation."""
 
         # Split the view line into exactly two words.  P4 enforces
         # structure on these lines that simplifies this quite a bit.
@@ -1934,76 +1836,62 @@
             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
+        # prefix - means exclude this path, leave out of mappings
         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)
+        if not exclude:
+            self.mappings.append(depot_side)
+
+    def convert_client_path(self, clientFile):
+        # chop off //client/ part to make it relative
+        if not clientFile.startswith(self.client_prefix):
+            die("No prefix '%s' on clientFile '%s'" %
+                (self.client_prefix, clientFile))
+        return clientFile[len(self.client_prefix):]
+
+    def update_client_spec_path_cache(self, files):
+        """ Caching file paths by "p4 where" batch query """
+
+        # List depot file paths exclude that already cached
+        fileArgs = [f['path'] for f in files if f['path'] not in self.client_spec_path_cache]
+
+        if len(fileArgs) == 0:
+            return  # All files in cache
+
+        where_result = p4CmdList(["-x", "-", "where"], stdin=fileArgs)
+        for res in where_result:
+            if "code" in res and res["code"] == "error":
+                # assume error is "... file(s) not in client view"
+                continue
+            if "clientFile" not in res:
+                die("No clientFile from 'p4 where %s'" % depot_path)
+            if "unmap" in res:
+                # it will list all of them, but only one not unmap-ped
+                continue
+            self.client_spec_path_cache[res['depotFile']] = self.convert_client_path(res["clientFile"])
+
+        # not found files or unmap files set to ""
+        for depotFile in fileArgs:
+            if depotFile not in self.client_spec_path_cache:
+                self.client_spec_path_cache[depotFile] = ""
 
     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 = ""
+        if depot_path in self.client_spec_path_cache:
+            return self.client_spec_path_cache[depot_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
+        die( "Error: %s is not found in client spec path" % depot_path )
+        return ""
 
 class P4Sync(Command, P4UserMap):
     delete_actions = ( "delete", "move/delete", "purge" )
@@ -2130,6 +2018,10 @@
         """Look at each depotFile in the commit to figure out to what
            branch it belongs."""
 
+        if self.clientSpecDirs:
+            files = self.extractFilesFromCommit(commit)
+            self.clientSpecDirs.update_client_spec_path_cache(files)
+
         branches = {}
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
@@ -2383,6 +2275,9 @@
             else:
                 sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
 
+        if self.clientSpecDirs:
+            self.clientSpecDirs.update_client_spec_path_cache(files)
+
         self.gitStream.write("commit %s\n" % branch)
 #        gitStream.write("mark :%s\n" % details["change"])
         self.committedChanges.add(int(details["change"]))
diff --git a/git-pull.sh b/git-pull.sh
index f0df41c..b946fd9 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -38,15 +38,19 @@
 test -z "$(git ls-files -u)" || die_conflict
 test -f "$GIT_DIR/MERGE_HEAD" && die_merge
 
+bool_or_string_config () {
+	git config --bool "$1" 2>/dev/null || git config "$1"
+}
+
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
 log_arg= verbosity= progress= recurse_submodules= verify_signatures=
-merge_args= edit=
+merge_args= edit= rebase_args=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
-rebase=$(git config --bool branch.$curr_branch_short.rebase)
+rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
 if test -z "$rebase"
 then
-	rebase=$(git config --bool pull.rebase)
+	rebase=$(bool_or_string_config pull.rebase)
 fi
 dry_run=
 while :
@@ -110,6 +114,9 @@
 		esac
 		merge_args="$merge_args$xx "
 		;;
+	-r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)
+		rebase="${1#*=}"
+		;;
 	-r|--r|--re|--reb|--reba|--rebas|--rebase)
 		rebase=true
 		;;
@@ -145,6 +152,20 @@
 	shift
 done
 
+case "$rebase" in
+preserve)
+	rebase=true
+	rebase_args=--preserve-merges
+	;;
+true|false|'')
+	;;
+*)
+	echo "Invalid value for --rebase, should be true, false, or preserve"
+	usage
+	exit 1
+	;;
+esac
+
 error_on_no_merge_candidates () {
 	exec >&2
 	for opt
@@ -166,9 +187,8 @@
 		op_prep=with
 	fi
 
-	curr_branch=${curr_branch#refs/heads/}
-	upstream=$(git config "branch.$curr_branch.merge")
-	remote=$(git config "branch.$curr_branch.remote")
+	upstream=$(git config "branch.$curr_branch_short.merge")
+	remote=$(git config "branch.$curr_branch_short.remote")
 
 	if [ $# -gt 1 ]; then
 		if [ "$rebase" = true ]; then
@@ -292,7 +312,7 @@
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
-	eval="git-rebase $diffstat $strategy_args $merge_args $verbosity"
+	eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
 	eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
 	;;
 *)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 83d6d46..10bf318 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -352,8 +352,9 @@
 			msg_content="$(commit_message $sha1)"
 			# No point in merging the first parent, that's HEAD
 			new_parents=${new_parents# $first_parent}
+			merge_args="--no-log --no-ff"
 			if ! do_with_author output eval \
-			'git merge --no-ff $strategy_args -m "$msg_content" $new_parents'
+			'git merge $merge_args $strategy_args -m "$msg_content" $new_parents'
 			then
 				printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
 				die_with_patch $sha1 "Error redoing merge $sha1"
@@ -671,7 +672,7 @@
 				;;
 			esac
 			;;
-		3,#*|3,)
+		3,"$comment_char"*|3,)
 			# copy comments
 			;;
 		*)
@@ -689,6 +690,32 @@
 	die "Could not skip unnecessary pick commands"
 }
 
+transform_todo_ids () {
+	while read -r command rest
+	do
+		case "$command" in
+		"$comment_char"* | exec)
+			# Be careful for oddball commands like 'exec'
+			# that do not have a SHA-1 at the beginning of $rest.
+			;;
+		*)
+			sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) &&
+			rest="$sha1 ${rest#* }"
+			;;
+		esac
+		printf '%s\n' "$command${rest:+ }$rest"
+	done <"$todo" >"$todo.new" &&
+	mv -f "$todo.new" "$todo"
+}
+
+expand_todo_ids() {
+	transform_todo_ids
+}
+
+collapse_todo_ids() {
+	transform_todo_ids --short=7
+}
+
 # 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
@@ -841,6 +868,7 @@
 edit-todo)
 	git stripspace --strip-comments <"$todo" >"$todo".new
 	mv -f "$todo".new "$todo"
+	collapse_todo_ids
 	append_todo_help
 	git stripspace --comment-lines >>"$todo" <<\EOF
 
@@ -852,6 +880,7 @@
 
 	git_sequence_editor "$todo" ||
 		die "Could not execute editor"
+	expand_todo_ids
 
 	exit
 	;;
@@ -1008,6 +1037,8 @@
 has_action "$todo" ||
 	die_abort "Nothing to do"
 
+expand_todo_ids
+
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
diff --git a/git-rebase.sh b/git-rebase.sh
index 8d7659a..226752f 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -167,13 +167,22 @@
 	rm -rf "$state_dir"
 }
 
-run_specific_rebase () {
+run_specific_rebase_internal () {
 	if [ "$interactive_rebase" = implied ]; then
 		GIT_EDITOR=:
 		export GIT_EDITOR
 		autosquash=
 	fi
+	# On FreeBSD, the shell's "return" returns from the current
+	# function, not from the current file inclusion.
+	# run_specific_rebase_internal has the file inclusion as a
+	# last statement, so POSIX and FreeBSD's return will do the
+	# same thing.
 	. git-rebase--$type
+}
+
+run_specific_rebase () {
+	run_specific_rebase_internal
 	ret=$?
 	if test $ret -eq 0
 	then
diff --git a/git-remote-testgit.sh b/git-remote-testgit.sh
index 2109070..6d2f282 100755
--- a/git-remote-testgit.sh
+++ b/git-remote-testgit.sh
@@ -38,6 +38,7 @@
 			echo "*export-marks $gitmarks"
 		fi
 		test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
+		test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
 		echo
 		;;
 	list)
diff --git a/git-remote-testpy.py b/git-remote-testpy.py
deleted file mode 100644
index ca67899..0000000
--- a/git-remote-testpy.py
+++ /dev/null
@@ -1,305 +0,0 @@
-#!/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
-
-if sys.hexversion < 0x02000000:
-    # string.encode() is the limiter
-    sys.stderr.write("git-remote-testgit: requires Python 2.0 or later.\n")
-    sys.exit(1)
-
-
-def encode_filepath(path):
-    """Encodes a Unicode file path to a byte string.
-
-    On Python 2 this is a no-op; on Python 3 we encode the string as
-    suggested by [1] which allows an exact round-trip from the command line
-    to the filesystem.
-
-    [1] http://docs.python.org/3/c-api/unicode.html#file-system-encoding
-
-    """
-    if sys.hexversion < 0x03000000:
-        return path
-    return path.encode(sys.getfilesystemencoding(), 'surrogateescape')
-
-
-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(encode_filepath(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, 'git.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().decode()
-        if line == '\n':
-            break
-        if not line.startswith('import '):
-            die("Expected import line.")
-
-        # strip of leading 'import '
-        ref = line[7:].strip()
-        refs.append(ref)
-
-    print("feature done")
-
-    if os.environ.get("GIT_REMOTE_TESTGIT_FAILURE"):
-        die('Told to fail')
-
-    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")
-
-    if os.environ.get("GIT_REMOTE_TESTGIT_FAILURE"):
-        die('Told to fail')
-
-    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.decode()
-
-    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
-
-    # Use binary mode since Python 3 does not permit unbuffered I/O in text
-    # mode.  Unbuffered I/O is required to avoid data that should be going
-    # to git-fast-import after an "export" command getting caught in our
-    # stdin buffer instead.
-    sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0)
-    while (more):
-        more = read_one_line(repo)
-
-if __name__ == '__main__':
-    sys.exit(main(sys.argv))
diff --git a/git-send-email.perl b/git-send-email.perl
index 2162478..3782c3b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1234,7 +1234,7 @@
 				if ($smtp->code == 220) {
 					$smtp = Net::SMTP::SSL->start_SSL($smtp,
 									  ssl_verify_params())
-						or die "STARTTLS failed! ".$smtp->message;
+						or die "STARTTLS failed! ".IO::Socket::SSL::errstr();
 					$smtp_encryption = '';
 					# Send EHLO again to receive fresh
 					# supported commands
diff --git a/git-submodule.sh b/git-submodule.sh
index 2979197..c17bef1 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -1149,18 +1149,7 @@
 			echo
 		fi
 		echo
-	done |
-	if test -n "$for_status"; then
-		if [ -n "$files" ]; then
-			gettextln "Submodules changed but not updated:" | git stripspace -c
-		else
-			gettextln "Submodule changes to be committed:" | git stripspace -c
-		fi
-		printf "\n" | git stripspace -c
-		git stripspace -c
-	else
-		cat
-	fi
+	done
 }
 #
 # List all submodules, prefixed with:
diff --git a/git.c b/git.c
index b3893e7..1188979 100644
--- a/git.c
+++ b/git.c
@@ -7,7 +7,7 @@
 #include "commit.h"
 
 const char git_usage_string[] =
-	"git [--version] [--help] [-c name=value]\n"
+	"git [--version] [--help] [-C <path>] [-c name=value]\n"
 	"           [--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"
@@ -165,6 +165,17 @@
 			set_alternate_shallow_file((*argv)[0]);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "-C")) {
+			if (*argc < 2) {
+				fprintf(stderr, "No directory given for -C.\n" );
+				usage(git_usage_string);
+			}
+			if (chdir((*argv)[1]))
+				die_errno("Cannot change to '%s'", (*argv)[1]);
+			if (envchanged)
+				*envchanged = 1;
+			(*argv)++;
+			(*argc)--;
 		} else {
 			fprintf(stderr, "Unknown option: %s\n", cmd);
 			usage(git_usage_string);
diff --git a/git_remote_helpers/.gitignore b/git_remote_helpers/.gitignore
deleted file mode 100644
index cf040af..0000000
--- a/git_remote_helpers/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/GIT-PYTHON-VERSION
-/build
-/dist
diff --git a/git_remote_helpers/Makefile b/git_remote_helpers/Makefile
deleted file mode 100644
index 3d12232..0000000
--- a/git_remote_helpers/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# 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])")
-
-py_version=$(shell $(PYTHON_PATH) -c \
-	'import sys; print("%i.%i" % sys.version_info[:2])')
-
-all: $(pysetupfile)
-	$(QUIET)test "$$(cat GIT-PYTHON-VERSION 2>/dev/null)" = "$(py_version)" || \
-	flags=--force; \
-	$(PYTHON_PATH) $(pysetupfile) $(QUIETSETUP) build $$flags
-	$(QUIET)echo "$(py_version)" >GIT-PYTHON-VERSION
-
-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 GIT-PYTHON-VERSION
diff --git a/git_remote_helpers/__init__.py b/git_remote_helpers/__init__.py
deleted file mode 100644
index 00f69cb..0000000
--- a/git_remote_helpers/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/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
deleted file mode 100644
index 1dbb1b0..0000000
--- a/git_remote_helpers/git/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import sys
-if sys.hexversion < 0x02040000:
-    # The limiter is the subprocess module
-    sys.stderr.write("git_remote_helpers: requires Python 2.4 or later.\n")
-    sys.exit(1)
diff --git a/git_remote_helpers/git/exporter.py b/git_remote_helpers/git/exporter.py
deleted file mode 100644
index 9ee5f96..0000000
--- a/git_remote_helpers/git/exporter.py
+++ /dev/null
@@ -1,58 +0,0 @@
-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
deleted file mode 100644
index 007a1bfd..0000000
--- a/git_remote_helpers/git/git.py
+++ /dev/null
@@ -1,678 +0,0 @@
-#!/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
deleted file mode 100644
index d3f90e1..0000000
--- a/git_remote_helpers/git/importer.py
+++ /dev/null
@@ -1,69 +0,0 @@
-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.
-
-        Note that the keys in the returned dictionary are byte strings as
-        read from git.
-        """
-        args = ["git", "--git-dir=" + gitdir, "for-each-ref", "refs/heads"]
-        lines = check_output(args).strip().split('\n'.encode('ascii'))
-        refs = {}
-        for line in lines:
-            value, name = line.split(' '.encode('ascii'))
-            name = name.strip('commit\t'.encode('ascii'))
-            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, 'testgit.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
deleted file mode 100644
index e700250..0000000
--- a/git_remote_helpers/git/non_local.py
+++ /dev/null
@@ -1,61 +0,0 @@
-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
deleted file mode 100644
index acbf8d7..0000000
--- a/git_remote_helpers/git/repo.py
+++ /dev/null
@@ -1,76 +0,0 @@
-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
deleted file mode 100644
index 4bff887..0000000
--- a/git_remote_helpers/setup.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[build]
-build_purelib = build/lib
-build_platlib = build/lib
diff --git a/git_remote_helpers/setup.py b/git_remote_helpers/setup.py
deleted file mode 100644
index 6de41de..0000000
--- a/git_remote_helpers/setup.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python
-
-"""Distutils build/install script for the git_remote_helpers package."""
-
-from distutils.core import setup
-
-# If building under Python3 we need to run 2to3 on the code, do this by
-# trying to import distutils' 2to3 builder, which is only available in
-# Python3.
-try:
-    from distutils.command.build_py import build_py_2to3 as build_py
-except ImportError:
-    # 2.x
-    from distutils.command.build_py import build_py
-
-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'],
-    cmdclass = {'build_py': build_py},
-)
diff --git a/git_remote_helpers/util.py b/git_remote_helpers/util.py
deleted file mode 100644
index fbbb01b..0000000
--- a/git_remote_helpers/util.py
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/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/gitweb/gitweb.perl b/gitweb/gitweb.perl
index f429f75..68c77f6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4035,8 +4035,8 @@
 	      $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->a({-href => href(action=>"search_help"),
+			     -title => "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',
@@ -6468,7 +6468,7 @@
 	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";
-        unless ($omit_owner) {
+        if ($owner and not $omit_owner) {
 	        print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
         }
 	if (defined $cd{'rfc2822'}) {
@@ -6631,6 +6631,7 @@
 			$hash_base, '--', $file_name
 			or die_error(500, "Open git-blame --porcelain failed");
 	}
+	binmode $fd, ':utf8';
 
 	# incremental blame data returns early
 	if ($format eq 'data') {
diff --git a/gitweb/static/gitweb.css b/gitweb/static/gitweb.css
index cb86d2d..3b4d833 100644
--- a/gitweb/static/gitweb.css
+++ b/gitweb/static/gitweb.css
@@ -68,12 +68,13 @@
 }
 
 div.page_footer {
-	height: 17px;
+	height: 22px;
 	padding: 4px 8px;
 	background-color: #d9d8d1;
 }
 
 div.page_footer_text {
+	line-height: 22px;
 	float: left;
 	color: #555555;
 	font-style: italic;
@@ -548,8 +549,7 @@
 
 a.rss_logo {
 	float: right;
-	padding: 3px 0px;
-	width: 35px;
+	padding: 3px 5px;
 	line-height: 10px;
 	border: 1px solid;
 	border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
diff --git a/http-backend.c b/http-backend.c
index 0324417..8c464bd 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -594,9 +594,11 @@
 
 			if (strcmp(method, c->method)) {
 				const char *proto = getenv("SERVER_PROTOCOL");
-				if (proto && !strcmp(proto, "HTTP/1.1"))
+				if (proto && !strcmp(proto, "HTTP/1.1")) {
 					http_status(405, "Method Not Allowed");
-				else
+					hdr_str("Allow", !strcmp(c->method, "GET") ?
+						"GET, HEAD" : c->method);
+				} else
 					http_status(400, "Bad Request");
 				hdr_nocache();
 				end_headers();
diff --git a/http-push.c b/http-push.c
index 6dad188..69200ba 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1330,8 +1330,7 @@
 			break;
 		}
 
-	free(tree->buffer);
-	tree->buffer = NULL;
+	free_tree_buffer(tree);
 	return p;
 }
 
@@ -1976,7 +1975,7 @@
 		pushing = 0;
 		if (prepare_revision_walk(&revs))
 			die("revision walk setup failed");
-		mark_edges_uninteresting(revs.commits, &revs, NULL);
+		mark_edges_uninteresting(&revs, NULL);
 		objects_to_send = get_delta(&revs, ref_lock);
 		finish_all_active_slots();
 
diff --git a/list-objects.c b/list-objects.c
index 3dd4a96..6cbedf0 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -123,8 +123,7 @@
 				     cb_data);
 	}
 	strbuf_setlen(base, baselen);
-	free(tree->buffer);
-	tree->buffer = NULL;
+	free_tree_buffer(tree);
 }
 
 static void mark_edge_parents_uninteresting(struct commit *commit,
@@ -145,19 +144,35 @@
 	}
 }
 
-void mark_edges_uninteresting(struct commit_list *list,
-			      struct rev_info *revs,
-			      show_edge_fn show_edge)
+void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
 {
-	for ( ; list; list = list->next) {
+	struct commit_list *list;
+	int i;
+
+	for (list = revs->commits; list; list = list->next) {
 		struct commit *commit = list->item;
 
 		if (commit->object.flags & UNINTERESTING) {
 			mark_tree_uninteresting(commit->tree);
+			if (revs->edge_hint && !(commit->object.flags & SHOWN)) {
+				commit->object.flags |= SHOWN;
+				show_edge(commit);
+			}
 			continue;
 		}
 		mark_edge_parents_uninteresting(commit, revs, show_edge);
 	}
+	for (i = 0; i < revs->cmdline.nr; i++) {
+		struct object *obj = revs->cmdline.rev[i].item;
+		struct commit *commit = (struct commit *)obj;
+		if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
+			continue;
+		mark_tree_uninteresting(commit->tree);
+		if (revs->edge_hint && !(obj->flags & SHOWN)) {
+			obj->flags |= SHOWN;
+			show_edge(commit);
+		}
+	}
 }
 
 static void add_pending_tree(struct rev_info *revs, struct tree *tree)
diff --git a/list-objects.h b/list-objects.h
index 3db7bb6..136a1da 100644
--- a/list-objects.h
+++ b/list-objects.h
@@ -6,6 +6,6 @@
 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);
+void mark_edges_uninteresting(struct rev_info *, show_edge_fn);
 
 #endif
diff --git a/mailmap.c b/mailmap.c
index 3061434..81890a6 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -52,6 +52,20 @@
 	string_list_clear_func(&me->namemap, free_mailmap_info);
 }
 
+/*
+ * On some systems (e.g. MinGW 4.0), string.h has _only_ inline
+ * definition of strcasecmp and no non-inline implementation is
+ * supplied anywhere, which is, eh, "unusual"; we cannot take an
+ * address of such a function to store it in namemap.cmp.  This is
+ * here as a workaround---do not assign strcasecmp directly to
+ * namemap.cmp until we know no systems that matter have such an
+ * "unusual" string.h.
+ */
+static int namemap_cmp(const char *a, const char *b)
+{
+	return strcasecmp(a, b);
+}
+
 static void add_mapping(struct string_list *map,
 			char *new_name, char *new_email,
 			char *old_name, char *old_email)
@@ -75,7 +89,7 @@
 		item = string_list_insert_at_index(map, index, old_email);
 		me = xcalloc(1, sizeof(struct mailmap_entry));
 		me->namemap.strdup_strings = 1;
-		me->namemap.cmp = strcasecmp;
+		me->namemap.cmp = namemap_cmp;
 		item->util = me;
 	}
 
@@ -153,8 +167,7 @@
 		if (!strncmp(buffer, abbrev, abblen)) {
 			char *cp;
 
-			if (repo_abbrev)
-				free(*repo_abbrev);
+			free(*repo_abbrev);
 			*repo_abbrev = xmalloc(len);
 
 			for (cp = buffer + abblen; isspace(*cp); cp++)
@@ -238,7 +251,7 @@
 	int err = 0;
 
 	map->strdup_strings = 1;
-	map->cmp = strcasecmp;
+	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
 		git_mailmap_blob = "HEAD:.mailmap";
diff --git a/notes-utils.h b/notes-utils.h
index b4cb1bf..564e30c 100644
--- a/notes-utils.h
+++ b/notes-utils.h
@@ -9,7 +9,7 @@
  * 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().
+ * - author/committer: the default determined by commit_tree().
  * - commit message: msg
  *
  * The resulting commit SHA1 is stored in result_sha1.
diff --git a/object.c b/object.c
index d8a4b1f..5f792cb 100644
--- a/object.c
+++ b/object.c
@@ -43,16 +43,17 @@
 	die("invalid object type \"%s\"", str);
 }
 
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
 {
 	unsigned int hash;
-	memcpy(&hash, obj->sha1, sizeof(unsigned int));
-	return hash % n;
+	memcpy(&hash, sha1, sizeof(unsigned int));
+	/* Assumes power-of-2 hash sizes in grow_object_hash */
+	return hash & (n - 1);
 }
 
 static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
 {
-	unsigned int j = hash_obj(obj, size);
+	unsigned int j = hash_obj(obj->sha1, size);
 
 	while (hash[j]) {
 		j++;
@@ -62,13 +63,6 @@
 	hash[j] = obj;
 }
 
-static unsigned int hashtable_index(const unsigned char *sha1)
-{
-	unsigned int i;
-	memcpy(&i, sha1, sizeof(unsigned int));
-	return i % obj_hash_size;
-}
-
 struct object *lookup_object(const unsigned char *sha1)
 {
 	unsigned int i, first;
@@ -77,7 +71,7 @@
 	if (!obj_hash)
 		return NULL;
 
-	first = i = hashtable_index(sha1);
+	first = i = hash_obj(sha1, obj_hash_size);
 	while ((obj = obj_hash[i]) != NULL) {
 		if (!hashcmp(sha1, obj->sha1))
 			break;
@@ -101,6 +95,10 @@
 static void grow_object_hash(void)
 {
 	int i;
+	/*
+	 * Note that this size must always be power-of-2 to match hash_obj
+	 * above.
+	 */
 	int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
 	struct object **new_hash;
 
diff --git a/pager.c b/pager.c
index c1ecf65..fa19765 100644
--- a/pager.c
+++ b/pager.c
@@ -54,7 +54,7 @@
 		pager = getenv("PAGER");
 	if (!pager)
 		pager = DEFAULT_PAGER;
-	else if (!*pager || !strcmp(pager, "cat"))
+	if (!*pager || !strcmp(pager, "cat"))
 		pager = NULL;
 
 	return pager;
diff --git a/perl/Git/SVN/Ra.pm b/perl/Git/SVN/Ra.pm
index 75ecc42..a7b0119 100644
--- a/perl/Git/SVN/Ra.pm
+++ b/perl/Git/SVN/Ra.pm
@@ -32,6 +32,14 @@
 	}
 }
 
+# serf has a bug that leads to a coredump upon termination if the
+# remote access object is left around (not fixed yet in serf 1.3.1).
+# Explicitly free it to work around the issue.
+END {
+	$RA = undef;
+	$ra_invalid = 1;
+}
+
 sub _auth_providers () {
 	my @rv = (
 	  SVN::Client::get_simple_provider(),
diff --git a/po/da.po b/po/da.po
index 20a88ea..898b18d 100644
--- a/po/da.po
+++ b/po/da.po
@@ -1674,7 +1674,7 @@
 msgstr ""
 
 #: builtin/describe.c:482
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:77
diff --git a/po/de.po b/po/de.po
index 11dde11..35a44b9 100644
--- a/po/de.po
+++ b/po/de.po
@@ -4684,8 +4684,8 @@
 msgstr "gibt Größenangaben in menschenlesbaren Format aus"
 
 #: builtin/describe.c:16
-msgid "git describe [options] <committish>*"
-msgstr "git describe [Optionen] <committish>*"
+msgid "git describe [options] <commit-ish>*"
+msgstr "git describe [Optionen] <commit-ish>*"
 
 #: builtin/describe.c:17
 msgid "git describe [options] --dirty"
@@ -4694,12 +4694,12 @@
 #: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
-msgstr "annotierter Tag %s ist nicht verfügbar"
+msgstr "annotiertes Tag %s ist nicht verfügbar"
 
 #: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
-msgstr "annotierter Tag %s hat keinen eingebetteten Namen"
+msgstr "annotiertes Tag %s hat keinen eingebetteten Namen"
 
 #: builtin/describe.c:243
 #, c-format
@@ -4765,7 +4765,7 @@
 
 #: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
-msgstr "findet den Tag, die nach Commit kommt"
+msgstr "findet das Tag, das nach Commit kommt"
 
 #: builtin/describe.c:410
 msgid "debug search strategy on stderr"
@@ -4777,7 +4777,7 @@
 
 #: builtin/describe.c:412
 msgid "use any tag, even unannotated"
-msgstr "verwendet jeden Tag, auch nicht-annotierte"
+msgstr "verwendet jedes Tag, auch nicht-annotierte"
 
 #: builtin/describe.c:413
 msgid "always use long format"
@@ -4822,7 +4822,7 @@
 msgstr "Keine Namen gefunden, kann nichts beschreiben."
 
 #: builtin/describe.c:489
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr "Die Option --dirty kann nicht mit Commits verwendet werden."
 
 #: builtin/diff.c:79
@@ -4880,7 +4880,7 @@
 
 #: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
-msgstr "erzeugt künstlich einen Tag-Ersteller, wenn der Tag keinen hat"
+msgstr "erzeugt künstlich einen Tag-Ersteller, wenn das Tag keinen hat"
 
 #: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
@@ -5013,7 +5013,7 @@
 
 #: builtin/fetch.c:324
 msgid "[new tag]"
-msgstr "[neuer Tag]"
+msgstr "[neues Tag]"
 
 #: builtin/fetch.c:327
 msgid "[new branch]"
@@ -7831,7 +7831,7 @@
 #: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil der Tag bereits\n"
+"Aktualisierungen wurden zurückgewiesen, weil das Tag bereits\n"
 "im Remote-Repository existiert."
 
 #: builtin/push.c:260
@@ -9244,7 +9244,7 @@
 
 #: builtin/tag.c:454
 msgid "annotated tag, needs a message"
-msgstr "annotierter Tag, benötigt eine Beschreibung"
+msgstr "annotiertes Tag, benötigt eine Beschreibung"
 
 #: builtin/tag.c:456
 msgid "tag message"
@@ -9252,15 +9252,15 @@
 
 #: builtin/tag.c:458
 msgid "annotated and GPG-signed tag"
-msgstr "annotierter und GPG-signierter Tag"
+msgstr "annotiertes und GPG-signiertes Tag"
 
 #: builtin/tag.c:462
 msgid "use another key to sign the tag"
-msgstr "verwendet einen anderen Schlüssel um den Tag zu signieren"
+msgstr "verwendet einen anderen Schlüssel um das Tag zu signieren"
 
 #: builtin/tag.c:463
 msgid "replace the tag if exists"
-msgstr "ersetzt den Tag, wenn er existiert"
+msgstr "ersetzt das Tag, wenn es existiert"
 
 #: builtin/tag.c:464
 msgid "show tag list in columns"
diff --git a/po/fr.po b/po/fr.po
index 2cdb6f9..49e0ec1 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -4291,7 +4291,7 @@
 msgstr ""
 
 #: builtin/describe.c:15
-msgid "git describe [options] <committish>*"
+msgid "git describe [options] <commit-ish>*"
 msgstr ""
 
 #: builtin/describe.c:16
@@ -4417,7 +4417,7 @@
 msgstr ""
 
 #: builtin/describe.c:481
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:79
diff --git a/po/git.pot b/po/git.pot
index cf1e446..c91e197 100644
--- a/po/git.pot
+++ b/po/git.pot
@@ -4335,7 +4335,7 @@
 msgstr ""
 
 #: builtin/describe.c:16
-msgid "git describe [options] <committish>*"
+msgid "git describe [options] <commit-ish>*"
 msgstr ""
 
 #: builtin/describe.c:17
@@ -4465,7 +4465,7 @@
 msgstr ""
 
 #: builtin/describe.c:489
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:79
diff --git a/po/it.po b/po/it.po
index fe61f1a..9080219 100644
--- a/po/it.po
+++ b/po/it.po
@@ -2476,7 +2476,7 @@
 msgstr ""
 
 #: builtin/describe.c:482
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:77
diff --git a/po/nl.po b/po/nl.po
index e1399e2..ad31c66 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -1669,7 +1669,7 @@
 msgstr ""
 
 #: builtin/describe.c:482
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:77
diff --git a/po/pt_PT.po b/po/pt_PT.po
index 517ec29..689ad1b 100644
--- a/po/pt_PT.po
+++ b/po/pt_PT.po
@@ -2312,7 +2312,7 @@
 msgstr "Nenhum nome encontrado, não descreve nada."
 
 #: builtin/describe.c:482
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr ""
 
 #: builtin/diff.c:77
diff --git a/po/sv.po b/po/sv.po
index 12dfca9..dcf579b 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -4560,7 +4560,7 @@
 msgstr "skriv storlekar i människoläsbart format"
 
 #: builtin/describe.c:16
-msgid "git describe [options] <committish>*"
+msgid "git describe [options] <commit-ish>*"
 msgstr "git describe [flaggor] <incheckning-igt>*"
 
 #: builtin/describe.c:17
@@ -4696,8 +4696,8 @@
 msgstr "Inga namn hittades, kan inte beskriva något."
 
 #: builtin/describe.c:489
-msgid "--dirty is incompatible with committishes"
-msgstr "--dirty är inkompatibelt med \"committish\"-värden"
+msgid "--dirty is incompatible with commit-ishes"
+msgstr "--dirty är inkompatibelt med \"commit-ish\"-värden"
 
 #: builtin/diff.c:79
 #, c-format
diff --git a/po/vi.po b/po/vi.po
index dd2d2a7..11b4912 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -4663,8 +4663,8 @@
 msgstr "hiển thị kích cỡ theo định dạng dành cho người đọc"
 
 #: builtin/describe.c:16
-msgid "git describe [options] <committish>*"
-msgstr "git describe [các-tùy-chọn] <committish>*"
+msgid "git describe [options] <commit-ish>*"
+msgstr "git describe [các-tùy-chọn] <commit-ish>*"
 
 #: builtin/describe.c:17
 msgid "git describe [options] --dirty"
@@ -4799,7 +4799,7 @@
 msgstr "Không tìm thấy các tên, không thể mô tả gì cả."
 
 #: builtin/describe.c:489
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr "--dirty là xung khắc với các tùy chọn dành cho chuyển giao (commit)"
 
 #: builtin/diff.c:79
diff --git a/po/zh_CN.po b/po/zh_CN.po
index b7b46f1..1a042af 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -4562,7 +4562,7 @@
 msgstr "以用户可读的格式显示大小"
 
 #: builtin/describe.c:16
-msgid "git describe [options] <committish>*"
+msgid "git describe [options] <commit-ish>*"
 msgstr "git describe [选项] <提交号>*"
 
 #: builtin/describe.c:17
@@ -4698,7 +4698,7 @@
 msgstr "没有发现名称,无法描述任何东西。"
 
 #: builtin/describe.c:489
-msgid "--dirty is incompatible with committishes"
+msgid "--dirty is incompatible with commit-ishes"
 msgstr "--dirty 不能与提交同时使用"
 
 #: builtin/diff.c:79
diff --git a/reachable.c b/reachable.c
index e7e6a1e..654a8c5 100644
--- a/reachable.c
+++ b/reachable.c
@@ -80,8 +80,7 @@
 		else
 			process_blob(lookup_blob(entry.sha1), p, &me, entry.path, cp);
 	}
-	free(tree->buffer);
-	tree->buffer = NULL;
+	free_tree_buffer(tree);
 }
 
 static void process_tag(struct tag *tag, struct object_array *p,
diff --git a/read-cache.c b/read-cache.c
index 885943a..6bbe1b1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1818,8 +1818,17 @@
 			continue;
 		if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
 			ce_smudge_racily_clean_entry(ce);
-		if (is_null_sha1(ce->sha1))
-			return error("cache entry has null sha1: %s", ce->name);
+		if (is_null_sha1(ce->sha1)) {
+			static const char msg[] = "cache entry has null sha1: %s";
+			static int allow = -1;
+
+			if (allow < 0)
+				allow = git_env_bool("GIT_ALLOW_NULL_SHA1", 0);
+			if (allow)
+				warning(msg, ce->name);
+			else
+				return error(msg, ce->name);
+		}
 		if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
 			return -1;
 	}
diff --git a/refs.c b/refs.c
index d78860c..ad5d66c 100644
--- a/refs.c
+++ b/refs.c
@@ -72,6 +72,10 @@
 {
 	int component_len, component_count = 0;
 
+	if (!strcmp(refname, "@"))
+		/* Refname is a single character '@'. */
+		return -1;
+
 	while (1) {
 		/* We are at the start of a path component. */
 		component_len = check_refname_component(refname, flags);
@@ -1951,7 +1955,7 @@
 static char *substitute_branch_name(const char **string, int *len)
 {
 	struct strbuf buf = STRBUF_INIT;
-	int ret = interpret_branch_name(*string, &buf);
+	int ret = interpret_branch_name(*string, *len, &buf);
 
 	if (ret == *len) {
 		size_t size;
@@ -2121,11 +2125,12 @@
 }
 
 struct ref_lock *lock_any_ref_for_update(const char *refname,
-					 const unsigned char *old_sha1, int flags)
+					 const unsigned char *old_sha1,
+					 int flags, int *type_p)
 {
 	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 		return NULL;
-	return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
+	return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
 }
 
 /*
@@ -2413,60 +2418,82 @@
 	return 0;
 }
 
-static int repack_without_ref(const char *refname)
+static int repack_without_refs(const char **refnames, int n)
 {
 	struct ref_dir *packed;
 	struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
 	struct string_list_item *ref_to_delete;
+	int i, removed = 0;
 
-	if (!get_packed_ref(refname))
-		return 0; /* refname does not exist in packed refs */
+	/* Look for a packed ref */
+	for (i = 0; i < n; i++)
+		if (get_packed_ref(refnames[i]))
+			break;
+
+	/* Avoid locking if we have nothing to do */
+	if (i == n)
+		return 0; /* no refname exists in packed refs */
 
 	if (lock_packed_refs(0)) {
 		unable_to_lock_error(git_path("packed-refs"), errno);
-		return error("cannot delete '%s' from packed refs", refname);
+		return error("cannot delete '%s' from packed refs", refnames[i]);
 	}
 	packed = get_packed_refs(&ref_cache);
 
-	/* Remove refname from the cache: */
-	if (remove_entry(packed, refname) == -1) {
+	/* Remove refnames from the cache */
+	for (i = 0; i < n; i++)
+		if (remove_entry(packed, refnames[i]) != -1)
+			removed = 1;
+	if (!removed) {
 		/*
-		 * The packed entry disappeared while we were
+		 * All packed entries disappeared while we were
 		 * acquiring the lock.
 		 */
 		rollback_packed_refs();
 		return 0;
 	}
 
-	/* Remove any other accumulated cruft: */
+	/* Remove any other accumulated cruft */
 	do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
 	for_each_string_list_item(ref_to_delete, &refs_to_delete) {
 		if (remove_entry(packed, ref_to_delete->string) == -1)
 			die("internal error");
 	}
 
-	/* Write what remains: */
+	/* Write what remains */
 	return commit_packed_refs();
 }
 
+static int repack_without_ref(const char *refname)
+{
+	return repack_without_refs(&refname, 1);
+}
+
+static int delete_ref_loose(struct ref_lock *lock, int flag)
+{
+	if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
+		/* loose */
+		int err, i = strlen(lock->lk->filename) - 5; /* .lock */
+
+		lock->lk->filename[i] = 0;
+		err = unlink_or_warn(lock->lk->filename);
+		lock->lk->filename[i] = '.';
+		if (err && errno != ENOENT)
+			return 1;
+	}
+	return 0;
+}
+
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 {
 	struct ref_lock *lock;
-	int err, i = 0, ret = 0, flag = 0;
+	int ret = 0, flag = 0;
 
 	lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
 	if (!lock)
 		return 1;
-	if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
-		/* loose */
-		i = strlen(lock->lk->filename) - 5; /* .lock */
-		lock->lk->filename[i] = 0;
-		err = unlink_or_warn(lock->lk->filename);
-		if (err && errno != ENOENT)
-			ret = 1;
+	ret |= delete_ref_loose(lock, flag);
 
-		lock->lk->filename[i] = '.';
-	}
 	/* removing the loose one could have resurrected an earlier
 	 * packed one.  Also, if it was not loose we need to repack
 	 * without it.
@@ -3169,12 +3196,13 @@
 	return retval;
 }
 
-int update_ref(const char *action, const char *refname,
-		const unsigned char *sha1, const unsigned char *oldval,
-		int flags, enum action_on_err onerr)
+static struct ref_lock *update_ref_lock(const char *refname,
+					const unsigned char *oldval,
+					int flags, int *type_p,
+					enum action_on_err onerr)
 {
-	static struct ref_lock *lock;
-	lock = lock_any_ref_for_update(refname, oldval, flags);
+	struct ref_lock *lock;
+	lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
 	if (!lock) {
 		const char *str = "Cannot lock the ref '%s'.";
 		switch (onerr) {
@@ -3182,8 +3210,14 @@
 		case DIE_ON_ERR: die(str, refname); break;
 		case QUIET_ON_ERR: break;
 		}
-		return 1;
 	}
+	return lock;
+}
+
+static int update_ref_write(const char *action, const char *refname,
+			    const unsigned char *sha1, struct ref_lock *lock,
+			    enum action_on_err onerr)
+{
 	if (write_ref_sha1(lock, sha1, action) < 0) {
 		const char *str = "Cannot update the ref '%s'.";
 		switch (onerr) {
@@ -3196,6 +3230,117 @@
 	return 0;
 }
 
+int update_ref(const char *action, const char *refname,
+	       const unsigned char *sha1, const unsigned char *oldval,
+	       int flags, enum action_on_err onerr)
+{
+	struct ref_lock *lock;
+	lock = update_ref_lock(refname, oldval, flags, 0, onerr);
+	if (!lock)
+		return 1;
+	return update_ref_write(action, refname, sha1, lock, onerr);
+}
+
+static int ref_update_compare(const void *r1, const void *r2)
+{
+	const struct ref_update * const *u1 = r1;
+	const struct ref_update * const *u2 = r2;
+	return strcmp((*u1)->ref_name, (*u2)->ref_name);
+}
+
+static int ref_update_reject_duplicates(struct ref_update **updates, int n,
+					enum action_on_err onerr)
+{
+	int i;
+	for (i = 1; i < n; i++)
+		if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
+			const char *str =
+				"Multiple updates for ref '%s' not allowed.";
+			switch (onerr) {
+			case MSG_ON_ERR:
+				error(str, updates[i]->ref_name); break;
+			case DIE_ON_ERR:
+				die(str, updates[i]->ref_name); break;
+			case QUIET_ON_ERR:
+				break;
+			}
+			return 1;
+		}
+	return 0;
+}
+
+int update_refs(const char *action, const struct ref_update **updates_orig,
+		int n, enum action_on_err onerr)
+{
+	int ret = 0, delnum = 0, i;
+	struct ref_update **updates;
+	int *types;
+	struct ref_lock **locks;
+	const char **delnames;
+
+	if (!updates_orig || !n)
+		return 0;
+
+	/* Allocate work space */
+	updates = xmalloc(sizeof(*updates) * n);
+	types = xmalloc(sizeof(*types) * n);
+	locks = xcalloc(n, sizeof(*locks));
+	delnames = xmalloc(sizeof(*delnames) * n);
+
+	/* Copy, sort, and reject duplicate refs */
+	memcpy(updates, updates_orig, sizeof(*updates) * n);
+	qsort(updates, n, sizeof(*updates), ref_update_compare);
+	ret = ref_update_reject_duplicates(updates, n, onerr);
+	if (ret)
+		goto cleanup;
+
+	/* Acquire all locks while verifying old values */
+	for (i = 0; i < n; i++) {
+		locks[i] = update_ref_lock(updates[i]->ref_name,
+					   (updates[i]->have_old ?
+					    updates[i]->old_sha1 : NULL),
+					   updates[i]->flags,
+					   &types[i], onerr);
+		if (!locks[i]) {
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	/* Perform updates first so live commits remain referenced */
+	for (i = 0; i < n; i++)
+		if (!is_null_sha1(updates[i]->new_sha1)) {
+			ret = update_ref_write(action,
+					       updates[i]->ref_name,
+					       updates[i]->new_sha1,
+					       locks[i], onerr);
+			locks[i] = NULL; /* freed by update_ref_write */
+			if (ret)
+				goto cleanup;
+		}
+
+	/* Perform deletes now that updates are safely completed */
+	for (i = 0; i < n; i++)
+		if (locks[i]) {
+			delnames[delnum++] = locks[i]->ref_name;
+			ret |= delete_ref_loose(locks[i], types[i]);
+		}
+	ret |= repack_without_refs(delnames, delnum);
+	for (i = 0; i < delnum; i++)
+		unlink_or_warn(git_path("logs/%s", delnames[i]));
+	clear_loose_ref_cache(&ref_cache);
+
+cleanup:
+	for (i = 0; i < n; i++)
+		if (locks[i])
+			unlock_ref(locks[i]);
+	free(updates);
+	free(types);
+	free(locks);
+	free(delnames);
+	return ret;
+}
+
 /*
  * generate a format suitable for scanf from a ref_rev_parse_rules
  * rule, that is replace the "%.*s" spec with a "%s" spec
diff --git a/refs.h b/refs.h
index 9e5db3a..b113377 100644
--- a/refs.h
+++ b/refs.h
@@ -10,6 +10,20 @@
 	int force_write;
 };
 
+/**
+ * Information needed for a single ref update.  Set new_sha1 to the
+ * new value or to zero to delete the ref.  To check the old value
+ * while locking the ref, set have_old to 1 and set old_sha1 to the
+ * value or to zero to ensure the ref does not exist before update.
+ */
+struct ref_update {
+	const char *ref_name;
+	unsigned char new_sha1[20];
+	unsigned char old_sha1[20];
+	int flags; /* REF_NODEREF? */
+	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+};
+
 /*
  * Bit values set in the flags argument passed to each_ref_fn():
  */
@@ -137,7 +151,7 @@
 #define REF_NODEREF	0x01
 extern struct ref_lock *lock_any_ref_for_update(const char *refname,
 						const unsigned char *old_sha1,
-						int flags);
+						int flags, int *type_p);
 
 /** Close the file descriptor owned by a lock and return the status */
 extern int close_ref(struct ref_lock *lock);
@@ -214,6 +228,12 @@
 		const unsigned char *sha1, const unsigned char *oldval,
 		int flags, enum action_on_err onerr);
 
+/**
+ * Lock all refs and then perform all modifications.
+ */
+int update_refs(const char *action, const struct ref_update **updates,
+		int n, enum action_on_err onerr);
+
 extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 extern int ref_is_hidden(const char *);
 
diff --git a/remote.c b/remote.c
index 2433467..e9fedfa 100644
--- a/remote.c
+++ b/remote.c
@@ -1729,7 +1729,11 @@
 }
 
 /*
- * Return true if there is anything to report, otherwise false.
+ * Compare a branch with its upstream, and save their differences (number
+ * of commits) in *num_ours and *num_theirs.
+ *
+ * Return 0 if branch has no upstream (no base), -1 if upstream is missing
+ * (with "gone" base), otherwise 1 (with base).
  */
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
 {
@@ -1740,34 +1744,30 @@
 	const char *rev_argv[10], *base;
 	int rev_argc;
 
-	/*
-	 * Nothing to report unless we are marked to build on top of
-	 * somebody else.
-	 */
+	/* Cannot stat unless we are marked to build on top of somebody else. */
 	if (!branch ||
 	    !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
 		return 0;
 
-	/*
-	 * If what we used to build on no longer exists, there is
-	 * nothing to report.
-	 */
+	/* Cannot stat if what we used to build on no longer exists */
 	base = branch->merge[0]->dst;
 	if (read_ref(base, sha1))
-		return 0;
+		return -1;
 	theirs = lookup_commit_reference(sha1);
 	if (!theirs)
-		return 0;
+		return -1;
 
 	if (read_ref(branch->refname, sha1))
-		return 0;
+		return -1;
 	ours = lookup_commit_reference(sha1);
 	if (!ours)
-		return 0;
+		return -1;
 
 	/* are we the same? */
-	if (theirs == ours)
-		return 0;
+	if (theirs == ours) {
+		*num_theirs = *num_ours = 0;
+		return 1;
+	}
 
 	/* Run "rev-list --left-right ours...theirs" internally... */
 	rev_argc = 0;
@@ -1809,31 +1809,53 @@
  */
 int format_tracking_info(struct branch *branch, struct strbuf *sb)
 {
-	int num_ours, num_theirs;
+	int ours, theirs;
 	const char *base;
+	int upstream_is_gone = 0;
 
-	if (!stat_tracking_info(branch, &num_ours, &num_theirs))
+	switch (stat_tracking_info(branch, &ours, &theirs)) {
+	case 0:
+		/* no base */
 		return 0;
+	case -1:
+		/* with "gone" base */
+		upstream_is_gone = 1;
+		break;
+	default:
+		/* with base */
+		break;
+	}
 
 	base = branch->merge[0]->dst;
 	base = shorten_unambiguous_ref(base, 0);
-	if (!num_theirs) {
+	if (upstream_is_gone) {
+		strbuf_addf(sb,
+			_("Your branch is based on '%s', but the upstream is gone.\n"),
+			base);
+		if (advice_status_hints)
+			strbuf_addf(sb,
+				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
+	} else if (!ours && !theirs) {
+		strbuf_addf(sb,
+			_("Your branch is up-to-date with '%s'.\n"),
+			base);
+	} else if (!theirs) {
 		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);
+			   ours),
+			base, ours);
 		if (advice_status_hints)
 			strbuf_addf(sb,
 				_("  (use \"git push\" to publish your local commits)\n"));
-	} else if (!num_ours) {
+	} else if (!ours) {
 		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);
+			   theirs),
+			base, theirs);
 		if (advice_status_hints)
 			strbuf_addf(sb,
 				_("  (use \"git pull\" to update your local branch)\n"));
@@ -1845,8 +1867,8 @@
 			   "Your branch and '%s' have diverged,\n"
 			       "and have %d and %d different commits each, "
 			       "respectively.\n",
-			   num_theirs),
-			base, num_ours, num_theirs);
+			   theirs),
+			base, ours, theirs);
 		if (advice_status_hints)
 			strbuf_addf(sb,
 				_("  (use \"git pull\" to merge the remote branch into yours)\n"));
diff --git a/revision.c b/revision.c
index 6230a80..0173e01 100644
--- a/revision.c
+++ b/revision.c
@@ -139,8 +139,7 @@
 	 * We don't care about the tree any more
 	 * after it has been marked uninteresting.
 	 */
-	free(tree->buffer);
-	tree->buffer = NULL;
+	free_tree_buffer(tree);
 }
 
 void mark_parents_uninteresting(struct commit *commit)
@@ -201,7 +200,7 @@
 		revs->no_walk = 0;
 	if (revs->reflog_info && obj->type == OBJ_COMMIT) {
 		struct strbuf buf = STRBUF_INIT;
-		int len = interpret_branch_name(name, &buf);
+		int len = interpret_branch_name(name, 0, &buf);
 		int st;
 
 		if (0 < len && name[len] && buf.len)
diff --git a/sequencer.c b/sequencer.c
index 351548f..06e52b4 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -279,7 +279,8 @@
 	read_cache();
 	if (checkout_fast_forward(from, to, 1))
 		exit(1); /* the callee should have complained already */
-	ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
+	ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
+					   0, NULL);
 	strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
 	ret = write_ref_sha1(ref_lock, to, sb.buf);
 	strbuf_release(&sb);
diff --git a/sha1-lookup.c b/sha1-lookup.c
index c4dc55d..2dd8515 100644
--- a/sha1-lookup.c
+++ b/sha1-lookup.c
@@ -204,7 +204,54 @@
 			 * byte 0 thru (ofs-1) are the same between
 			 * lo and hi; ofs is the first byte that is
 			 * different.
+			 *
+			 * If ofs==20, then no bytes are different,
+			 * meaning we have entries with duplicate
+			 * keys. We know that we are in a solid run
+			 * of this entry (because the entries are
+			 * sorted, and our lo and hi are the same,
+			 * there can be nothing but this single key
+			 * in between). So we can stop the search.
+			 * Either one of these entries is it (and
+			 * we do not care which), or we do not have
+			 * it.
+			 *
+			 * Furthermore, we know that one of our
+			 * endpoints must be the edge of the run of
+			 * duplicates. For example, given this
+			 * sequence:
+			 *
+			 *     idx 0 1 2 3 4 5
+			 *     key A C C C C D
+			 *
+			 * If we are searching for "B", we might
+			 * hit the duplicate run at lo=1, hi=3
+			 * (e.g., by first mi=3, then mi=0). But we
+			 * can never have lo > 1, because B < C.
+			 * That is, if our key is less than the
+			 * run, we know that "lo" is the edge, but
+			 * we can say nothing of "hi". Similarly,
+			 * if our key is greater than the run, we
+			 * know that "hi" is the edge, but we can
+			 * say nothing of "lo".
+			 *
+			 * Therefore if we do not find it, we also
+			 * know where it would go if it did exist:
+			 * just on the far side of the edge that we
+			 * know about.
 			 */
+			if (ofs == 20) {
+				mi = lo;
+				mi_key = base + elem_size * mi + key_offset;
+				cmp = memcmp(mi_key, key, 20);
+				if (!cmp)
+					return mi;
+				if (cmp < 0)
+					return -1 - hi;
+				else
+					return -1 - lo;
+			}
+
 			hiv = hi_key[ofs_0];
 			if (ofs_0 < 19)
 				hiv = (hiv << 8) | hi_key[ofs_0+1];
diff --git a/sha1_file.c b/sha1_file.c
index 8c2d1ed..f80bbe4 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2126,6 +2126,16 @@
 		int i;
 		struct delta_base_cache_entry *ent;
 
+		ent = get_delta_base_cache_entry(p, curpos);
+		if (eq_delta_base_cache_entry(ent, p, curpos)) {
+			type = ent->type;
+			data = ent->data;
+			size = ent->size;
+			clear_delta_base_cache_entry(ent);
+			base_from_cache = 1;
+			break;
+		}
+
 		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;
@@ -2140,16 +2150,6 @@
 			}
 		}
 
-		ent = get_delta_base_cache_entry(p, curpos);
-		if (eq_delta_base_cache_entry(ent, p, curpos)) {
-			type = ent->type;
-			data = ent->data;
-			size = ent->size;
-			clear_delta_base_cache_entry(ent);
-			base_from_cache = 1;
-			break;
-		}
-
 		type = unpack_object_header(p, &w_curs, &curpos, &size);
 		if (type != OBJ_OFS_DELTA && type != OBJ_REF_DELTA)
 			break;
@@ -2995,7 +2995,10 @@
 
 	if (find_pack_entry(sha1, &e))
 		return 1;
-	return has_loose_object(sha1);
+	if (has_loose_object(sha1))
+		return 1;
+	reprepare_packed_git();
+	return find_pack_entry(sha1, &e);
 }
 
 static void check_tree(const void *buf, size_t size)
diff --git a/sha1_name.c b/sha1_name.c
index 65ad066..0e5fe7f 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -677,11 +677,13 @@
 		return -1;
 
 	sp++; /* beginning of type name, or closing brace for empty */
-	if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
+	if (!prefixcmp(sp, "commit}"))
 		expected_type = OBJ_COMMIT;
-	else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
+	else if (!prefixcmp(sp, "tag}"))
+		expected_type = OBJ_TAG;
+	else if (!prefixcmp(sp, "tree}"))
 		expected_type = OBJ_TREE;
-	else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
+	else if (!prefixcmp(sp, "blob}"))
 		expected_type = OBJ_BLOB;
 	else if (!prefixcmp(sp, "object}"))
 		expected_type = OBJ_ANY;
@@ -1004,6 +1006,28 @@
 	return st;
 }
 
+/* parse @something syntax, when 'something' is not {.*} */
+static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf)
+{
+	const char *next;
+
+	if (len || name[1] == '{')
+		return -1;
+
+	/* make sure it's a single @, or @@{.*}, not @foo */
+	next = strchr(name + len + 1, '@');
+	if (next && next[1] != '{')
+		return -1;
+	if (!next)
+		next = name + namelen;
+	if (next != name + 1)
+		return -1;
+
+	strbuf_reset(buf);
+	strbuf_add(buf, "HEAD", 4);
+	return 1;
+}
+
 static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
 {
 	/* we have extra data, which might need further processing */
@@ -1012,7 +1036,7 @@
 	int ret;
 
 	strbuf_add(buf, name + len, namelen - len);
-	ret = interpret_branch_name(buf->buf, &tmp);
+	ret = interpret_branch_name(buf->buf, buf->len, &tmp);
 	/* that data was not interpreted, remove our cruft */
 	if (ret < 0) {
 		strbuf_setlen(buf, used);
@@ -1046,14 +1070,16 @@
  * 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)
+int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
 {
 	char *cp;
 	struct branch *upstream;
-	int namelen = strlen(name);
 	int len = interpret_nth_prior_checkout(name, buf);
 	int tmp_len;
 
+	if (!namelen)
+		namelen = strlen(name);
+
 	if (!len) {
 		return len; /* syntax Ok, not enough switches */
 	} else if (len > 0) {
@@ -1066,9 +1092,15 @@
 	cp = strchr(name, '@');
 	if (!cp)
 		return -1;
+
+	len = interpret_empty_at(name, namelen, cp - name, buf);
+	if (len > 0)
+		return reinterpret(name, namelen, len, buf);
+
 	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);
@@ -1100,7 +1132,7 @@
 int strbuf_branchname(struct strbuf *sb, const char *name)
 {
 	int len = strlen(name);
-	int used = interpret_branch_name(name, sb);
+	int used = interpret_branch_name(name, len, sb);
 
 	if (used == len)
 		return 0;
@@ -1130,13 +1162,13 @@
 }
 
 /*
- * Many callers know that the user meant to name a committish by
+ * Many callers know that the user meant to name a commit-ish 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.
+ * abbreviated object names between commit-ish 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
+ * commit-ish. It is merely to give a hint to the disambiguation
  * machinery.
  */
 int get_sha1_committish(const char *name, unsigned char *sha1)
diff --git a/shallow.c b/shallow.c
index 8a9c96d..cdf37d6 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "commit.h"
 #include "tag.h"
+#include "pkt-line.h"
 
 static int is_shallow = -1;
 static struct stat shallow_stat;
@@ -141,3 +142,81 @@
 		   )
 		die("shallow file was changed during fetch");
 }
+
+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);
+	if (graft->nr_parent != -1)
+		return 0;
+	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;
+}
+
+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;
+}
+
+char *setup_temporary_shallow(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int fd;
+
+	if (write_shallow_commits(&sb, 0)) {
+		struct strbuf path = STRBUF_INIT;
+		strbuf_addstr(&path, git_path("shallow_XXXXXX"));
+		fd = xmkstemp(path.buf);
+		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+			die_errno("failed to write to %s",
+				  path.buf);
+		close(fd);
+		strbuf_release(&sb);
+		return strbuf_detach(&path, NULL);
+	}
+	/*
+	 * is_repository_shallow() sees empty string as "no shallow
+	 * file".
+	 */
+	return xstrdup("");
+}
+
+void setup_alternate_shallow(struct lock_file *shallow_lock,
+			     const char **alternate_shallow_file)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int fd;
+
+	check_shallow_file_for_update();
+	fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
+				       LOCK_DIE_ON_ERROR);
+	if (write_shallow_commits(&sb, 0)) {
+		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+			die_errno("failed to write to %s",
+				  shallow_lock->filename);
+		*alternate_shallow_file = shallow_lock->filename;
+	} else
+		/*
+		 * is_repository_shallow() sees empty string as "no
+		 * shallow file".
+		 */
+		*alternate_shallow_file = "";
+	strbuf_release(&sb);
+}
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index 2098b9b..ccd918e 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -48,7 +48,8 @@
 P4PORT=localhost:$P4DPORT
 P4CLIENT=client
 P4EDITOR=:
-export P4PORT P4CLIENT P4EDITOR
+unset P4CHARSET
+export P4PORT P4CLIENT P4EDITOR P4CHARSET
 
 db="$TRASH_DIRECTORY/db"
 cli="$TRASH_DIRECTORY/cli"
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index dab405d..54dbbfe 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -169,7 +169,7 @@
 		test_i18ngrep "Updates were rejected because" output
 	'
 
-	test_expect_failure 'force with lease aka cas' '
+	test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' '
 		HEAD=$(	cd "$REMOTE_REPO" && git rev-parse --verify HEAD ) &&
 		test_when_finished '\''
 			(cd "$REMOTE_REPO" && git update-ref HEAD "$HEAD")
diff --git a/t/lib-pack.sh b/t/lib-pack.sh
new file mode 100644
index 0000000..7e8685b
--- /dev/null
+++ b/t/lib-pack.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# Support routines for hand-crafting weird or malicious packs.
+#
+# You can make a complete pack like:
+#
+#   pack_header 2 >foo.pack &&
+#   pack_obj e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 >>foo.pack &&
+#   pack_obj e68fe8129b546b101aee9510c5328e7f21ca1d18 >>foo.pack &&
+#   pack_trailer foo.pack
+
+# Print the big-endian 4-byte octal representation of $1
+uint32_octal () {
+	n=$1
+	printf '\%o' $(($n / 16777216)); n=$((n % 16777216))
+	printf '\%o' $(($n /    65536)); n=$((n %    65536))
+	printf '\%o' $(($n /      256)); n=$((n %      256))
+	printf '\%o' $(($n           ));
+}
+
+# Print the big-endian 4-byte binary representation of $1
+uint32_binary () {
+	printf "$(uint32_octal "$1")"
+}
+
+# Print a pack header, version 2, for a pack with $1 objects
+pack_header () {
+	printf 'PACK' &&
+	printf '\0\0\0\2' &&
+	uint32_binary "$1"
+}
+
+# Print the pack data for object $1, as a delta against object $2 (or as a full
+# object if $2 is missing or empty). The output is suitable for including
+# directly in the packfile, and represents the entirety of the object entry.
+# Doing this on the fly (especially picking your deltas) is quite tricky, so we
+# have hardcoded some well-known objects. See the case statements below for the
+# complete list.
+pack_obj () {
+	case "$1" in
+	# empty blob
+	e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+		case "$2" in
+		'')
+			printf '\060\170\234\003\0\0\0\0\1'
+			return
+			;;
+		esac
+		;;
+
+	# blob containing "\7\76"
+	e68fe8129b546b101aee9510c5328e7f21ca1d18)
+		case "$2" in
+		'')
+			printf '\062\170\234\143\267\3\0\0\116\0\106'
+			return
+			;;
+		01d7713666f4de822776c7622c10f1b07de280dc)
+			printf '\165\1\327\161\66\146\364\336\202\47\166' &&
+			printf '\307\142\54\20\361\260\175\342\200\334\170' &&
+			printf '\234\143\142\142\142\267\003\0\0\151\0\114'
+			return
+			;;
+		esac
+		;;
+
+	# blob containing "\7\0"
+	01d7713666f4de822776c7622c10f1b07de280dc)
+		case "$2" in
+		'')
+			printf '\062\170\234\143\147\0\0\0\20\0\10'
+			return
+			;;
+		e68fe8129b546b101aee9510c5328e7f21ca1d18)
+			printf '\165\346\217\350\22\233\124\153\20\32\356' &&
+			printf '\225\20\305\62\216\177\41\312\35\30\170\234' &&
+			printf '\143\142\142\142\147\0\0\0\53\0\16'
+			return
+			;;
+		esac
+		;;
+	esac
+
+	echo >&2 "BUG: don't know how to print $1${2:+ (from $2)}"
+	return 1
+}
+
+# Compute and append pack trailer to "$1"
+pack_trailer () {
+	test-sha1 -b <"$1" >trailer.tmp &&
+	cat trailer.tmp >>"$1" &&
+	rm -f trailer.tmp
+}
+
+# Remove any existing packs to make sure that
+# whatever we index next will be the pack that we
+# actually use.
+clear_packs () {
+	rm -f .git/objects/pack/*
+}
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index ad66410..9fb582b 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -379,6 +379,10 @@
 	test -d realgitdir/refs
 '
 
+test_expect_success 're-init on .git file' '
+	( cd newdir && git init )
+'
+
 test_expect_success 're-init to update git link' '
 	(
 	cd newdir &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 96f40fe..181513a 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -66,11 +66,11 @@
 
 	init_vars &&
 	rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
-	echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
+	echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
 		>"$HOME/cmd" &&
 	echo "$expect_code" >"$HOME/expected-exit-code" &&
 	test_expect_code "$expect_code" \
-		git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
+		git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \
 		>"$HOME/stdout" 2>"$HOME/stderr" &&
 	test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
 	stderr_empty_on_success "$expect_code"
@@ -87,6 +87,9 @@
 # check-ignore --verbose output is the same as normal output except
 # for the extra first column.
 #
+# A parameter is used to determine if the tests are run with the
+# normal case (using the index), or with the --no-index option.
+#
 # Arguments:
 #   - (optional) prereqs for this test, e.g. 'SYMLINKS'
 #   - test name
@@ -94,19 +97,26 @@
 #     from the other verbosity modes is automatically inferred
 #     from this value)
 #   - code to run (should invoke test_check_ignore)
-test_expect_success_multi () {
+#   - index option: --index or --no-index
+test_expect_success_multiple () {
 	prereq=
-	if test $# -eq 4
+	if test $# -eq 5
 	then
 		prereq=$1
 		shift
 	fi
+	if test "$4" = "--index"
+	then
+		no_index_opt=
+	else
+		no_index_opt=$4
+	fi
 	testname="$1" expect_all="$2" code="$3"
 
 	expect_verbose=$( echo "$expect_all" | grep -v '^::	' )
 	expect=$( echo "$expect_verbose" | sed -e 's/.*	//' )
 
-	test_expect_success $prereq "$testname" '
+	test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" '
 		expect "$expect" &&
 		eval "$code"
 	'
@@ -116,7 +126,8 @@
 	then
 		for quiet_opt in '-q' '--quiet'
 		do
-			test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
+			opts="${no_index_opt:+$no_index_opt }$quiet_opt"
+			test_expect_success $prereq "$testname${opts:+ with $opts}" "
 			expect '' &&
 			$code
 		"
@@ -126,7 +137,7 @@
 
 	for verbose_opt in '-v' '--verbose'
 	do
-		for non_matching_opt in '' ' -n' ' --non-matching'
+		for non_matching_opt in '' '-n' '--non-matching'
 		do
 			if test -n "$non_matching_opt"
 			then
@@ -139,12 +150,21 @@
 				expect '$my_expect' &&
 				$code
 			"
-			opts="$verbose_opt$non_matching_opt"
+			opts="${no_index_opt:+$no_index_opt }$verbose_opt${non_matching_opt:+ $non_matching_opt}"
 			test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
 		done
 	done
 	verbose_opt=
 	non_matching_opt=
+	no_index_opt=
+}
+
+test_expect_success_multi () {
+	test_expect_success_multiple "$@" "--index"
+}
+
+test_expect_success_no_index_multi () {
+	test_expect_success_multiple "$@" "--no-index"
 }
 
 test_expect_success 'setup' '
@@ -288,7 +308,7 @@
 
 # First make sure that the presence of a file in the working tree
 # does not impact results, but that the presence of a file in the
-# index does.
+# index does unless the --no-index option is used.
 
 for subdir in '' 'a/'
 do
@@ -303,22 +323,42 @@
 		"::	${subdir}non-existent" \
 		"test_check_ignore '${subdir}non-existent' 1"
 
+	test_expect_success_no_index_multi "non-existent file $where not ignored" \
+		"::	${subdir}non-existent" \
+		"test_check_ignore '${subdir}non-existent' 1"
+
 	test_expect_success_multi "non-existent file $where ignored" \
 		".gitignore:1:one	${subdir}one" \
 		"test_check_ignore '${subdir}one'"
 
+	test_expect_success_no_index_multi "non-existent file $where ignored" \
+		".gitignore:1:one	${subdir}one" \
+		"test_check_ignore '${subdir}one'"
+
 	test_expect_success_multi "existing untracked file $where not ignored" \
 		"::	${subdir}not-ignored" \
 		"test_check_ignore '${subdir}not-ignored' 1"
 
+	test_expect_success_no_index_multi "existing untracked file $where not ignored" \
+		"::	${subdir}not-ignored" \
+		"test_check_ignore '${subdir}not-ignored' 1"
+
 	test_expect_success_multi "existing tracked file $where not ignored" \
 		"::	${subdir}ignored-but-in-index" \
 		"test_check_ignore '${subdir}ignored-but-in-index' 1"
 
+	test_expect_success_no_index_multi "existing tracked file $where shown as ignored" \
+		".gitignore:2:ignored-*	${subdir}ignored-but-in-index" \
+		"test_check_ignore '${subdir}ignored-but-in-index'"
+
 	test_expect_success_multi "existing untracked file $where ignored" \
 		".gitignore:2:ignored-*	${subdir}ignored-and-untracked" \
 		"test_check_ignore '${subdir}ignored-and-untracked'"
 
+	test_expect_success_no_index_multi "existing untracked file $where ignored" \
+		".gitignore:2:ignored-*	${subdir}ignored-and-untracked" \
+		"test_check_ignore '${subdir}ignored-and-untracked'"
+
 	test_expect_success_multi "mix of file types $where" \
 "::	${subdir}non-existent
 .gitignore:1:one	${subdir}one
@@ -332,6 +372,20 @@
 			${subdir}ignored-but-in-index
 			${subdir}ignored-and-untracked'
 		"
+
+	test_expect_success_no_index_multi "mix of file types $where" \
+"::	${subdir}non-existent
+.gitignore:1:one	${subdir}one
+::	${subdir}not-ignored
+.gitignore:2:ignored-*	${subdir}ignored-but-in-index
+.gitignore:2:ignored-*	${subdir}ignored-and-untracked" \
+		"test_check_ignore '
+			${subdir}non-existent
+			${subdir}one
+			${subdir}not-ignored
+			${subdir}ignored-but-in-index
+			${subdir}ignored-and-untracked'
+		"
 done
 
 # Having established the above, from now on we mostly test against
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 05d78d2..6b3cedc 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -91,6 +91,7 @@
 test_expect_success "setup unicode normalization tests" '
 	test_create_repo unicode &&
 	cd unicode &&
+	git config core.precomposeunicode false &&
 	touch "$aumlcdiar" &&
 	git add "$aumlcdiar" &&
 	git commit -m initial &&
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
new file mode 100755
index 0000000..99c0377
--- /dev/null
+++ b/t/t0056-git-C.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='"-C <path>" option and its effects on other path-related options'
+
+. ./test-lib.sh
+
+test_expect_success '"git -C <path>" runs git from the directory <path>' '
+	test_create_repo dir1 &&
+	echo 1 >dir1/a.txt &&
+	msg="initial in dir1" &&
+	(cd dir1 && git add a.txt && git commit -m "$msg") &&
+	echo "$msg" >expected &&
+	git -C dir1 log --format=%s >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Multiple -C options: "-C dir1 -C dir2" is equivalent to "-C dir1/dir2"' '
+	test_create_repo dir1/dir2 &&
+	echo 1 >dir1/dir2/b.txt &&
+	git -C dir1/dir2 add b.txt &&
+	msg="initial in dir1/dir2" &&
+	echo "$msg" >expected &&
+	git -C dir1/dir2 commit -m "$msg" &&
+	git -C dir1 -C dir2 log --format=%s >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Effect on --git-dir option: "-C c --git-dir=a.git" is equivalent to "--git-dir c/a.git"' '
+	mkdir c &&
+	mkdir c/a &&
+	mkdir c/a.git &&
+	(cd c/a.git && git init --bare) &&
+	echo 1 >c/a/a.txt &&
+	git --git-dir c/a.git --work-tree=c/a add a.txt &&
+	git --git-dir c/a.git --work-tree=c/a commit -m "initial" &&
+	git --git-dir=c/a.git log -1 --format=%s >expected &&
+	git -C c --git-dir=a.git log -1 --format=%s >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "--git-dir=a.git -C c" is equivalent to "-C c --git-dir=a.git"' '
+	git -C c --git-dir=a.git log -1 --format=%s >expected &&
+	git --git-dir=a.git -C c log -1 --format=%s >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Effect on --work-tree option: "-C c/a.git --work-tree=../a"  is equivalent to "--work-tree=c/a --git-dir=c/a.git"' '
+	rm c/a/a.txt &&
+	git --git-dir=c/a.git --work-tree=c/a status >expected &&
+	git -C c/a.git --work-tree=../a status >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "--work-tree=../a -C c/a.git" is equivalent to "-C c/a.git --work-tree=../a"' '
+	git -C c/a.git --work-tree=../a status >expected &&
+	git --work-tree=../a -C c/a.git status >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Effect on --git-dir and --work-tree options - "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=c/a.git --work-tree=c/a"' '
+	git --git-dir=c/a.git --work-tree=c/a status >expected &&
+	git -C c --git-dir=a.git --work-tree=a status >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git -C c --work-tree=a"' '
+	git -C c --git-dir=a.git --work-tree=a status >expected &&
+	git --git-dir=a.git -C c --work-tree=a status >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git --work-tree=a -C c"' '
+	git -C c --git-dir=a.git --work-tree=a status >expected &&
+	git --git-dir=a.git --work-tree=a -C c status >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success 'Relative followed by fullpath: "-C ./here -C /there" is equivalent to "-C /there"' '
+	echo "initial in dir1/dir2" >expected &&
+	git -C dir1 -C "$(pwd)/dir1/dir2" log --format=%s >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index c23f478..9673593 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -652,16 +652,23 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--int is at least 64 bits' '
+	git config giga.watts 121g &&
+	echo 129922760704 >expect &&
+	git config --int --get giga.watts >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'invalid unit' '
 	git config aninvalid.unit "1auto" &&
 	echo 1auto >expect &&
 	git config aninvalid.unit >actual &&
 	test_cmp expect actual &&
-	cat > expect <<-\EOF
-	fatal: bad config value for '\''aninvalid.unit'\'' in .git/config
+	cat >expect <<-\EOF
+	fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit
 	EOF
 	test_must_fail git config --int --get aninvalid.unit 2>actual &&
-	test_cmp actual expect
+	test_i18ncmp expect actual
 '
 
 cat > expect << EOF
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index e415ee0..6ffd82f 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -302,4 +302,636 @@
 	'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
 	'test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")'
 
+a=refs/heads/a
+b=refs/heads/b
+c=refs/heads/c
+E='""'
+F='%s\0'
+pws='path with space'
+
+test_expect_success 'stdin test setup' '
+	echo "$pws" >"$pws" &&
+	git add -- "$pws" &&
+	git commit -m "$pws"
+'
+
+test_expect_success '-z fails without --stdin' '
+	test_must_fail git update-ref -z $m $m $m 2>err &&
+	grep "usage: git update-ref" err
+'
+
+test_expect_success 'stdin works with no input' '
+	>stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse --verify -q $m
+'
+
+test_expect_success 'stdin fails on empty line' '
+	echo "" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: empty command in input" err
+'
+
+test_expect_success 'stdin fails on only whitespace' '
+	echo " " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: whitespace before command:  " err
+'
+
+test_expect_success 'stdin fails on leading whitespace' '
+	echo " create $a $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: whitespace before command:  create $a $m" err
+'
+
+test_expect_success 'stdin fails on unknown command' '
+	echo "unknown $a" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: unknown command: unknown $a" err
+'
+
+test_expect_success 'stdin fails on badly quoted input' '
+	echo "create $a \"master" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: badly quoted argument: \\\"master" err
+'
+
+test_expect_success 'stdin fails on arguments not separated by space' '
+	echo "create \"$a\"master" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: expected SP but got: master" err
+'
+
+test_expect_success 'stdin fails create with no ref' '
+	echo "create " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: create line missing <ref>" err
+'
+
+test_expect_success 'stdin fails create with bad ref name' '
+	echo "create ~a $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'stdin fails create with no new value' '
+	echo "create $a" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: create $a missing <newvalue>" err
+'
+
+test_expect_success 'stdin fails create with too many arguments' '
+	echo "create $a $m $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: create $a has extra input:  $m" err
+'
+
+test_expect_success 'stdin fails update with no ref' '
+	echo "update " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: update line missing <ref>" err
+'
+
+test_expect_success 'stdin fails update with bad ref name' '
+	echo "update ~a $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'stdin fails update with no new value' '
+	echo "update $a" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: update $a missing <newvalue>" err
+'
+
+test_expect_success 'stdin fails update with too many arguments' '
+	echo "update $a $m $m $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: update $a has extra input:  $m" err
+'
+
+test_expect_success 'stdin fails delete with no ref' '
+	echo "delete " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: delete line missing <ref>" err
+'
+
+test_expect_success 'stdin fails delete with bad ref name' '
+	echo "delete ~a $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'stdin fails delete with too many arguments' '
+	echo "delete $a $m $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: delete $a has extra input:  $m" err
+'
+
+test_expect_success 'stdin fails verify with too many arguments' '
+	echo "verify $a $m $m" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: verify $a has extra input:  $m" err
+'
+
+test_expect_success 'stdin fails option with unknown name' '
+	echo "option unknown" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: option unknown: unknown" err
+'
+
+test_expect_success 'stdin fails with duplicate refs' '
+	cat >stdin <<-EOF &&
+	create $a $m
+	create $b $m
+	create $a $m
+	EOF
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+'
+
+test_expect_success 'stdin create ref works' '
+	echo "create $a $m" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin update ref creates with zero old value' '
+	echo "update $b $m $Z" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	git update-ref -d $b
+'
+
+test_expect_success 'stdin update ref creates with empty old value' '
+	echo "update $b $m $E" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin create ref works with path with space to blob' '
+	echo "create refs/blobs/pws \"$m:$pws\"" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse "$m:$pws" >expect &&
+	git rev-parse refs/blobs/pws >actual &&
+	test_cmp expect actual &&
+	git update-ref -d refs/blobs/pws
+'
+
+test_expect_success 'stdin update ref fails with wrong old value' '
+	echo "update $c $m $m~1" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update ref fails with bad old value' '
+	echo "update $c $m does-not-exist" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: invalid old value for ref $c: does-not-exist" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin create ref fails with bad new value' '
+	echo "create $c does-not-exist" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: invalid new value for ref $c: does-not-exist" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin create ref fails with zero new value' '
+	echo "create $c " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: create $c given zero new value" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update ref works with right old value' '
+	echo "update $b $m~1 $m" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref fails with wrong old value' '
+	echo "delete $a $m~1" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$a'"'"'" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref fails with zero old value' '
+	echo "delete $a " >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: delete $a given zero old value" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin update symref works option no-deref' '
+	git symbolic-ref TESTSYMREF $b &&
+	cat >stdin <<-EOF &&
+	option no-deref
+	update TESTSYMREF $a $b
+	EOF
+	git update-ref --stdin <stdin &&
+	git rev-parse TESTSYMREF >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin delete symref works option no-deref' '
+	git symbolic-ref TESTSYMREF $b &&
+	cat >stdin <<-EOF &&
+	option no-deref
+	delete TESTSYMREF $b
+	EOF
+	git update-ref --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q TESTSYMREF &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin delete ref works with right old value' '
+	echo "delete $b $m~1" >stdin &&
+	git update-ref --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q $b
+'
+
+test_expect_success 'stdin update/create/verify combination works' '
+	cat >stdin <<-EOF &&
+	update $a $m
+	create $b $m
+	verify $c
+	EOF
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update refs works with identity updates' '
+	cat >stdin <<-EOF &&
+	update $a $m $m
+	update $b $m $m
+	update $c $Z $E
+	EOF
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin update refs fails with wrong old value' '
+	git update-ref $c $m &&
+	cat >stdin <<-EOF &&
+	update $a $m $m
+	update $b $m $m
+	update $c  ''
+	EOF
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	git rev-parse $c >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin delete refs works with packed and loose refs' '
+	git pack-refs --all &&
+	git update-ref $c $m~1 &&
+	cat >stdin <<-EOF &&
+	delete $a $m
+	update $b $Z $m
+	update $c $E $m~1
+	EOF
+	git update-ref --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q $a &&
+	test_must_fail git rev-parse --verify -q $b &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z works on empty input' '
+	>stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse --verify -q $m
+'
+
+test_expect_success 'stdin -z fails on empty line' '
+	echo "" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: whitespace before command: " err
+'
+
+test_expect_success 'stdin -z fails on empty command' '
+	printf $F "" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: empty command in input" err
+'
+
+test_expect_success 'stdin -z fails on only whitespace' '
+	printf $F " " >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: whitespace before command:  " err
+'
+
+test_expect_success 'stdin -z fails on leading whitespace' '
+	printf $F " create $a" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: whitespace before command:  create $a" err
+'
+
+test_expect_success 'stdin -z fails on unknown command' '
+	printf $F "unknown $a" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: unknown command: unknown $a" err
+'
+
+test_expect_success 'stdin -z fails create with no ref' '
+	printf $F "create " >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: create line missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails create with bad ref name' '
+	printf $F "create ~a " "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a " err
+'
+
+test_expect_success 'stdin -z fails create with no new value' '
+	printf $F "create $a" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: create $a missing <newvalue>" err
+'
+
+test_expect_success 'stdin -z fails create with too many arguments' '
+	printf $F "create $a" "$m" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails update with no ref' '
+	printf $F "update " >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: update line missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails update with bad ref name' '
+	printf $F "update ~a" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'stdin -z fails update with no new value' '
+	printf $F "update $a" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: update $a missing <newvalue>" err
+'
+
+test_expect_success 'stdin -z fails update with no old value' '
+	printf $F "update $a" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: update $a missing \\[<oldvalue>\\] NUL" err
+'
+
+test_expect_success 'stdin -z fails update with too many arguments' '
+	printf $F "update $a" "$m" "$m" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails delete with no ref' '
+	printf $F "delete " >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: delete line missing <ref>" err
+'
+
+test_expect_success 'stdin -z fails delete with bad ref name' '
+	printf $F "delete ~a" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: invalid ref format: ~a" err
+'
+
+test_expect_success 'stdin -z fails delete with no old value' '
+	printf $F "delete $a" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: delete $a missing \\[<oldvalue>\\] NUL" err
+'
+
+test_expect_success 'stdin -z fails delete with too many arguments' '
+	printf $F "delete $a" "$m" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails verify with too many arguments' '
+	printf $F "verify $a" "$m" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: unknown command: $m" err
+'
+
+test_expect_success 'stdin -z fails verify with no old value' '
+	printf $F "verify $a" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: verify $a missing \\[<oldvalue>\\] NUL" err
+'
+
+test_expect_success 'stdin -z fails option with unknown name' '
+	printf $F "option unknown" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: option unknown: unknown" err
+'
+
+test_expect_success 'stdin -z fails with duplicate refs' '
+	printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err
+'
+
+test_expect_success 'stdin -z create ref works' '
+	printf $F "create $a" "$m" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z update ref creates with zero old value' '
+	printf $F "update $b" "$m" "$Z" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	git update-ref -d $b
+'
+
+test_expect_success 'stdin -z update ref creates with empty old value' '
+	printf $F "update $b" "$m" "" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z create ref works with path with space to blob' '
+	printf $F "create refs/blobs/pws" "$m:$pws" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse "$m:$pws" >expect &&
+	git rev-parse refs/blobs/pws >actual &&
+	test_cmp expect actual &&
+	git update-ref -d refs/blobs/pws
+'
+
+test_expect_success 'stdin -z update ref fails with wrong old value' '
+	printf $F "update $c" "$m" "$m~1" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update ref fails with bad old value' '
+	printf $F "update $c" "$m" "does-not-exist" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: invalid old value for ref $c: does-not-exist" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z create ref fails with bad new value' '
+	printf $F "create $c" "does-not-exist" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: invalid new value for ref $c: does-not-exist" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z create ref fails with zero new value' '
+	printf $F "create $c" "" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: create $c given zero new value" err &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update ref works with right old value' '
+	printf $F "update $b" "$m~1" "$m" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref fails with wrong old value' '
+	printf $F "delete $a" "$m~1" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$a'"'"'" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref fails with zero old value' '
+	printf $F "delete $a" "$Z" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: delete $a given zero old value" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z update symref works option no-deref' '
+	git symbolic-ref TESTSYMREF $b &&
+	printf $F "option no-deref" "update TESTSYMREF" "$a" "$b" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse TESTSYMREF >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete symref works option no-deref' '
+	git symbolic-ref TESTSYMREF $b &&
+	printf $F "option no-deref" "delete TESTSYMREF" "$b" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q TESTSYMREF &&
+	git rev-parse $m~1 >expect &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete ref works with right old value' '
+	printf $F "delete $b" "$m~1" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q $b
+'
+
+test_expect_success 'stdin -z update/create/verify combination works' '
+	printf $F "update $a" "$m" "" "create $b" "$m" "verify $c" "" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update refs works with identity updates' '
+	printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$Z" "" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
+test_expect_success 'stdin -z update refs fails with wrong old value' '
+	git update-ref $c $m &&
+	printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "" "$Z" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual &&
+	git rev-parse $b >actual &&
+	test_cmp expect actual &&
+	git rev-parse $c >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin -z delete refs works with packed and loose refs' '
+	git pack-refs --all &&
+	git update-ref $c $m~1 &&
+	printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "" "$m~1" >stdin &&
+	git update-ref -z --stdin <stdin &&
+	test_must_fail git rev-parse --verify -q $a &&
+	test_must_fail git rev-parse --verify -q $b &&
+	test_must_fail git rev-parse --verify -q $c
+'
+
 test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index e5aea3b..ceb8449 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -32,6 +32,9 @@
 	git checkout -b upstream-branch &&
 	test_commit upstream-one &&
 	test_commit upstream-two &&
+	git checkout -b @/at-test &&
+	git checkout -b @@/at-test &&
+	git checkout -b @at-test &&
 	git checkout -b old-branch &&
 	test_commit old-one &&
 	test_commit old-two &&
@@ -55,6 +58,11 @@
 check "@{u}@{1}" commit upstream-one
 check "@{-1}@{u}" ref refs/heads/master
 check "@{-1}@{u}@{1}" commit master-one
+check "@" commit new-two
+check "@@{u}" ref refs/heads/upstream-branch
+check "@@/at-test" ref refs/heads/@@/at-test
+check "@/at-test" ref refs/heads/@/at-test
+check "@at-test" ref refs/heads/@at-test
 nonsense "@{u}@{-1}"
 nonsense "@{0}@{0}"
 nonsense "@{1}@{u}"
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index eaefc77..15973f2 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -54,6 +54,13 @@
 	test_must_fail git rev-parse blob-tag^{tree}
 '
 
+test_expect_success 'ref^{tag}' '
+	test_must_fail git rev-parse HEAD^{tag} &&
+	git rev-parse commit-tag >expected &&
+	git rev-parse commit-tag^{tag} >actual &&
+	test_cmp expected actual
+'
+
 test_expect_success 'ref^{/.}' '
 	git rev-parse master >expected &&
 	git rev-parse master^{/.} >actual &&
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index dee55e4..094b92e 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -104,7 +104,7 @@
 		cd repo_c &&
 		test_commit c_master &&
 		git checkout -b bar &&
-		test_commit c_bar
+		test_commit c_bar &&
 		git checkout -b spam &&
 		test_commit c_spam
 	) &&
@@ -113,9 +113,9 @@
 		cd repo_d &&
 		test_commit d_master &&
 		git checkout -b baz &&
-		test_commit f_baz
+		test_commit d_baz &&
 		git checkout -b eggs &&
-		test_commit c_eggs
+		test_commit d_eggs
 	) &&
 	git remote add repo_c repo_c &&
 	git config remote.repo_c.fetch \
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index f0421c0..b2798fe 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -115,7 +115,7 @@
 
 git config core.excludesFile excludes-file
 
-git status | grep "^#	" > output
+git -c status.displayCommentPrefix=true status | grep "^#	" > output
 
 cat > expect << EOF
 #	.gitignore
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
index f611d79..6d3b828 100755
--- a/t/t3010-ls-files-killed-modified.sh
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -11,6 +11,7 @@
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    pathx/ju    - a file in a directory
     submod1/	- a submodule
     submod2/	- another submodule
 
@@ -23,6 +24,7 @@
     path4	- a file
     path5	- a symlink
     path6/file6 - a file in a directory
+    pathx/ju/nk - a file in a directory to be killed
     submod1/	- a submodule (modified from the cache)
     submod2/	- a submodule (matches the cache)
 
@@ -44,14 +46,15 @@
 test_expect_success 'git update-index --add to add various paths.' '
 	date >path0 &&
 	test_ln_s_add xyzzy path1 &&
-	mkdir path2 path3 &&
+	mkdir path2 path3 pathx &&
 	date >path2/file2 &&
 	date >path3/file3 &&
+	>pathx/ju &&
 	: >path7 &&
 	date >path8 &&
 	: >path9 &&
 	date >path10 &&
-	git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+	git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 &&
 	for i in 1 2
 	do
 		git init submod$i &&
@@ -77,7 +80,7 @@
 		date >path3 &&
 		date >path5
 	fi &&
-	mkdir path0 path1 path6 &&
+	mkdir -p path0 path1 path6 pathx/ju &&
 	date >path0/file0 &&
 	date >path1/file1 &&
 	date >path6/file6 &&
@@ -85,16 +88,23 @@
 	: >path8 &&
 	: >path9 &&
 	touch path10 &&
-	git ls-files -k >.output
-'
-
-test_expect_success 'validate git ls-files -k output.' '
-	cat >.expected <<-\EOF &&
+	>pathx/ju/nk &&
+	cat >.expected <<-\EOF
 	path0/file0
 	path1/file1
 	path2
 	path3
+	pathx/ju/nk
 	EOF
+'
+
+test_expect_success 'git ls-files -k output (w/o icase)' '
+	git ls-files -k >.output
+	test_cmp .expected .output
+'
+
+test_expect_success 'git ls-files -k output (w/ icase)' '
+	git -c core.ignorecase=true ls-files -k >.output
 	test_cmp .expected .output
 '
 
@@ -110,6 +120,7 @@
 	path3/file3
 	path7
 	path8
+	pathx/ju
 	submod1
 	EOF
 	test_cmp .expected .output
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 44ec6a4..0fe7647 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -14,7 +14,8 @@
 	echo World >>A &&
 	git update-index --add A &&
 	git commit -m "Second commit." &&
-	HEAD=$(git rev-parse --verify HEAD)'
+	HEAD=$(git rev-parse --verify HEAD)
+'
 
 test_expect_success 'git branch --help should not have created a bogus branch' '
 	test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null &&
@@ -319,8 +320,9 @@
 
 test_expect_success 'tracking setup fails on non-matching refspec' '
 	git config remote.local.url . &&
-	git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+	git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
 	(git show-ref -q refs/remotes/local/master || git fetch local) &&
+	git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
 	test_must_fail git branch --track my5 local/master &&
 	test_must_fail git config branch.my5.remote &&
 	test_must_fail git config branch.my5.merge
@@ -350,7 +352,7 @@
 test_expect_success 'no tracking without .fetch entries' '
 	git config branch.autosetupmerge true &&
 	git branch my6 s &&
-	git config branch.automsetupmerge false &&
+	git config branch.autosetupmerge false &&
 	test -z "$(git config branch.my6.remote)" &&
 	test -z "$(git config branch.my6.merge)"
 '
@@ -424,14 +426,14 @@
 test_expect_success 'use --set-upstream-to modify HEAD' '
 	test_config branch.master.remote foo &&
 	test_config branch.master.merge foo &&
-	git branch my12
+	git branch my12 &&
 	git branch --set-upstream-to my12 &&
 	test "$(git config branch.master.remote)" = "." &&
 	test "$(git config branch.master.merge)" = "refs/heads/my12"
 '
 
 test_expect_success 'use --set-upstream-to modify a particular branch' '
-	git branch my13
+	git branch my13 &&
 	git branch --set-upstream-to master my13 &&
 	test "$(git config branch.my13.remote)" = "." &&
 	test "$(git config branch.my13.merge)" = "refs/heads/master"
@@ -442,7 +444,7 @@
 '
 
 test_expect_success 'test --unset-upstream on HEAD' '
-	git branch my14
+	git branch my14 &&
 	test_config branch.master.remote foo &&
 	test_config branch.master.merge foo &&
 	git branch --set-upstream-to my14 &&
@@ -464,7 +466,7 @@
 '
 
 test_expect_success 'test --unset-upstream on a particular branch' '
-	git branch my15
+	git branch my15 &&
 	git branch --set-upstream-to master my14 &&
 	git branch --unset-upstream my14 &&
 	test_must_fail git config branch.my14.remote &&
@@ -870,4 +872,39 @@
 	test_must_fail git branch --merged 0000000000000000000000000000000000000000
 '
 
+test_expect_success 'tracking with unexpected .fetch refspec' '
+	rm -rf a b c d &&
+	git init a &&
+	(
+		cd a &&
+		test_commit a
+	) &&
+	git init b &&
+	(
+		cd b &&
+		test_commit b
+	) &&
+	git init c &&
+	(
+		cd c &&
+		test_commit c &&
+		git remote add a ../a &&
+		git remote add b ../b &&
+		git fetch --all
+	) &&
+	git init d &&
+	(
+		cd d &&
+		git remote add c ../c &&
+		git config remote.c.fetch "+refs/remotes/*:refs/remotes/*" &&
+		git fetch c &&
+		git branch --track local/a/master remotes/a/master &&
+		test "$(git config branch.local/a/master.remote)" = "c" &&
+		test "$(git config branch.local/a/master.merge)" = "refs/remotes/a/master" &&
+		git rev-parse --verify a >expect &&
+		git rev-parse --verify local/a/master >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 4dbeddb..50e22b1 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -29,8 +29,6 @@
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
-set_fake_editor
-
 # WARNING: Modifications to the initial repository can change the SHA ID used
 # in the expect2 file for the 'stop on conflicting pick' test.
 
@@ -72,6 +70,7 @@
 test_expect_success 'rebase -i with the exec command' '
 	git checkout master &&
 	(
+	set_fake_editor &&
 	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" &&
@@ -93,6 +92,7 @@
 test_expect_success 'rebase -i with the exec command runs from tree root' '
 	git checkout master &&
 	mkdir subdir && (cd subdir &&
+	set_fake_editor &&
 	FAKE_LINES="1 exec_>touch-subdir" \
 		git rebase -i HEAD^
 	) &&
@@ -103,6 +103,7 @@
 test_expect_success 'rebase -i with the exec command checks tree cleanness' '
 	git checkout master &&
 	(
+	set_fake_editor &&
 	FAKE_LINES="exec_echo_foo_>file1 1" &&
 	export FAKE_LINES &&
 	test_must_fail git rebase -i HEAD^
@@ -116,6 +117,7 @@
 	git checkout master &&
 	test_when_finished "git rebase --abort" &&
 	(
+	set_fake_editor &&
 	FAKE_LINES="exec_this-command-does-not-exist 1" &&
 	export FAKE_LINES &&
 	test_must_fail git rebase -i HEAD^ >actual 2>&1
@@ -125,6 +127,7 @@
 
 test_expect_success 'no changes are a nop' '
 	git checkout branch2 &&
+	set_fake_editor &&
 	git rebase -i F &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
 	test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -134,6 +137,7 @@
 	git checkout -b dead-end &&
 	git rm file6 &&
 	git commit -m "stop here" &&
+	set_fake_editor &&
 	git rebase -i F branch2 &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
 	test $(git rev-parse I) = $(git rev-parse branch2) &&
@@ -142,6 +146,7 @@
 
 test_expect_success 'test --onto <branch>' '
 	git checkout -b test-onto branch2 &&
+	set_fake_editor &&
 	git rebase -i --onto branch1 F &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
 	test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
@@ -151,6 +156,7 @@
 test_expect_success 'rebase on top of a non-conflicting commit' '
 	git checkout branch1 &&
 	git tag original-branch1 &&
+	set_fake_editor &&
 	git rebase -i branch2 &&
 	test file6 = $(git diff --name-only original-branch1) &&
 	test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
@@ -163,6 +169,7 @@
 '
 
 test_expect_success 'exchange two commits' '
+	set_fake_editor &&
 	FAKE_LINES="2 1" git rebase -i HEAD~2 &&
 	test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
 	test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -188,6 +195,7 @@
 
 test_expect_success 'stop on conflicting pick' '
 	git tag new-branch1 &&
+	set_fake_editor &&
 	test_must_fail git rebase -i master &&
 	test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
 	test_cmp expect .git/rebase-merge/patch &&
@@ -208,6 +216,7 @@
 test_expect_success 'abort with error when new base cannot be checked out' '
 	git rm --cached file1 &&
 	git commit -m "remove file in base" &&
+	set_fake_editor &&
 	test_must_fail git rebase -i master > output 2>&1 &&
 	grep "The following untracked working tree files would be overwritten by checkout:" \
 		output &&
@@ -222,6 +231,7 @@
 	test_tick &&
 	GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
 	git tag twerp &&
+	set_fake_editor &&
 	git rebase -i --onto master HEAD^ &&
 	git show HEAD | grep "^Author: Twerp Snog"
 '
@@ -232,6 +242,7 @@
 	test_tick &&
 	GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
 	echo "******************************" &&
+	set_fake_editor &&
 	FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
 		git rebase -i --onto master HEAD~2 &&
 	test B = $(cat file7) &&
@@ -244,6 +255,7 @@
 
 test_expect_success '-p handles "no changes" gracefully' '
 	HEAD=$(git rev-parse HEAD) &&
+	set_fake_editor &&
 	git rebase -i -p HEAD^ &&
 	git update-index --refresh &&
 	git diff-files --quiet &&
@@ -253,6 +265,7 @@
 
 test_expect_failure 'exchange two commits with -p' '
 	git checkout H &&
+	set_fake_editor &&
 	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)
@@ -287,6 +300,7 @@
 	git commit -m M file1 &&
 	git checkout -b to-be-rebased &&
 	test_tick &&
+	set_fake_editor &&
 	git rebase -i -p --onto branch1 master &&
 	git update-index --refresh &&
 	git diff-files --quiet &&
@@ -301,6 +315,7 @@
 '
 
 test_expect_success 'edit ancestor with -p' '
+	set_fake_editor &&
 	FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
 	echo 2 > unrelated-file &&
 	test_tick &&
@@ -314,6 +329,7 @@
 
 test_expect_success '--continue tries to commit' '
 	test_tick &&
+	set_fake_editor &&
 	test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
 	echo resolved > file1 &&
 	git add file1 &&
@@ -325,6 +341,7 @@
 test_expect_success 'verbose flag is heeded, even after --continue' '
 	git reset --hard master@{1} &&
 	test_tick &&
+	set_fake_editor &&
 	test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
 	echo resolved > file1 &&
 	git add file1 &&
@@ -334,6 +351,7 @@
 
 test_expect_success 'multi-squash only fires up editor once' '
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
 		EXPECT_HEADER_COUNT=4 \
 		git rebase -i $base &&
@@ -344,6 +362,7 @@
 test_expect_success 'multi-fixup does not fire up editor' '
 	git checkout -b multi-fixup E &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
 		git rebase -i $base &&
 	test $base = $(git rev-parse HEAD^) &&
@@ -355,6 +374,7 @@
 test_expect_success 'commit message used after conflict' '
 	git checkout -b conflict-fixup conflict-branch &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	(
 		FAKE_LINES="1 fixup 3 fixup 4" &&
 		export FAKE_LINES &&
@@ -373,6 +393,7 @@
 test_expect_success 'commit message retained after conflict' '
 	git checkout -b conflict-squash conflict-branch &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	(
 		FAKE_LINES="1 fixup 3 squash 4" &&
 		export FAKE_LINES &&
@@ -399,6 +420,7 @@
 test_expect_success 'squash and fixup generate correct log messages' '
 	git checkout -b squash-fixup E &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
 		EXPECT_HEADER_COUNT=4 \
 		git rebase -i $base &&
@@ -411,6 +433,7 @@
 test_expect_success 'squash ignores comments' '
 	git checkout -b skip-comments E &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
 		EXPECT_HEADER_COUNT=4 \
 		git rebase -i $base &&
@@ -423,6 +446,7 @@
 test_expect_success 'squash ignores blank lines' '
 	git checkout -b skip-blank-lines E &&
 	base=$(git rev-parse HEAD~4) &&
+	set_fake_editor &&
 	FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
 		EXPECT_HEADER_COUNT=4 \
 		git rebase -i $base &&
@@ -435,6 +459,7 @@
 test_expect_success 'squash works as expected' '
 	git checkout -b squash-works no-conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
+	set_fake_editor &&
 	FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
 		git rebase -i HEAD~3 &&
 	test $one = $(git rev-parse HEAD~2)
@@ -443,6 +468,7 @@
 test_expect_success 'interrupted squash works as expected' '
 	git checkout -b interrupted-squash conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
+	set_fake_editor &&
 	(
 		FAKE_LINES="1 squash 3 2" &&
 		export FAKE_LINES &&
@@ -460,6 +486,7 @@
 test_expect_success 'interrupted squash works as expected (case 2)' '
 	git checkout -b interrupted-squash2 conflict-branch &&
 	one=$(git rev-parse HEAD~3) &&
+	set_fake_editor &&
 	(
 		FAKE_LINES="3 squash 1 2" &&
 		export FAKE_LINES &&
@@ -484,6 +511,7 @@
 	git commit -m "unrelated change" &&
 	parent=$(git rev-parse HEAD^) &&
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 	echo edited > file7 &&
 	git add file7 &&
@@ -496,6 +524,7 @@
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
 	old=$(git rev-parse HEAD) &&
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 	echo "edited again" > file7 &&
 	git add file7 &&
@@ -510,6 +539,7 @@
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 	echo "edited again" > file7 &&
 	git add file7 &&
@@ -528,6 +558,7 @@
 test_expect_success 'clean error after failed "exec"' '
 	test_tick &&
 	test_when_finished "git rebase --abort || :" &&
+	set_fake_editor &&
 	(
 		FAKE_LINES="1 exec_false" &&
 		export FAKE_LINES &&
@@ -543,6 +574,7 @@
 	grandparent=$(git rev-parse HEAD~2) &&
 	git checkout $(git rev-parse HEAD) &&
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES="2 1" git rebase -i HEAD~2 &&
 	test $grandparent = $(git rev-parse HEAD~2)
 '
@@ -559,6 +591,7 @@
 	test_must_fail git commit -m doesnt-verify file1 &&
 	git commit -m doesnt-verify --no-verify file1 &&
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES=2 git rebase -i HEAD~2
 
 '
@@ -580,6 +613,7 @@
 		git commit -m "Add body"
 	) &&
 
+	set_fake_editor &&
 	FAKE_LINES="1 squash 2" git rebase -i to-be-rebased &&
 	test "$(git show -s --pretty=format:%an)" = "Squashed Away"
 
@@ -591,6 +625,7 @@
 	GIT_EDITOR=: git commit --amend \
 		--author="Somebody else <somebody@else.com>" &&
 	test $(git rev-parse branch3) != $(git rev-parse branch4) &&
+	set_fake_editor &&
 	git rebase -i branch3 &&
 	test $(git rev-parse branch3) = $(git rev-parse branch4)
 
@@ -615,10 +650,12 @@
 		git commit -a -m "submodule second"
 	) &&
 	test_tick &&
+	set_fake_editor &&
 	git commit -a -m "Three changes submodule"
 '
 
 test_expect_success 'submodule rebase -i' '
+	set_fake_editor &&
 	FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
@@ -636,6 +673,7 @@
 '
 
 test_expect_success 'rebase -i continue with only submodule staged' '
+	set_fake_editor &&
 	test_must_fail git rebase -i submodule-base &&
 	git add sub &&
 	git rebase --continue &&
@@ -645,6 +683,7 @@
 test_expect_success 'rebase -i continue with unstaged submodule' '
 	git checkout submodule-topic &&
 	git reset --hard &&
+	set_fake_editor &&
 	test_must_fail git rebase -i submodule-base &&
 	git reset &&
 	git rebase --continue &&
@@ -657,6 +696,7 @@
 	test-chmtime =123456789 file3 &&
 	git update-index --refresh &&
 	HEAD=$(git rev-parse HEAD) &&
+	set_fake_editor &&
 	git rebase -i HEAD~4 &&
 	test $HEAD = $(git rev-parse HEAD) &&
 	MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
@@ -665,6 +705,7 @@
 
 test_expect_success 'reword' '
 	git checkout -b reword-branch master &&
+	set_fake_editor &&
 	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) &&
@@ -684,6 +725,7 @@
 	test_commit n2 &&
 	test_commit n3 &&
 	git notes add -m"a note" n3 &&
+	set_fake_editor &&
 	git rebase -i --onto n1 n2 &&
 	test "a note" = "$(git notes show HEAD)"
 '
@@ -697,6 +739,7 @@
 test_expect_success 'rebase -i can copy notes over a fixup' '
 	git reset --hard n3 &&
 	git notes add -m"an earlier note" n2 &&
+	set_fake_editor &&
 	GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
 	git notes show > output &&
 	test_cmp expect output
@@ -706,6 +749,7 @@
 	git symbolic-ref HEAD &&
 	grandparent=$(git rev-parse HEAD~2) &&
 	test_tick &&
+	set_fake_editor &&
 	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
@@ -715,6 +759,7 @@
 test_expect_success 'always cherry-pick with --no-ff' '
 	git checkout no-ff-branch &&
 	git tag original-no-ff-branch &&
+	set_fake_editor &&
 	git rebase -i --no-ff A &&
 	touch empty &&
 	for p in 0 1 2
@@ -747,6 +792,7 @@
 test_expect_success 'rebase-i history with funny messages' '
 	git rev-list A..funny >expect &&
 	test_tick &&
+	set_fake_editor &&
 	FAKE_LINES="1 2 3 4" git rebase -i A &&
 	git rev-list A.. >actual &&
 	test_cmp expect actual
@@ -763,6 +809,7 @@
 
 
 test_expect_success 'running "git rebase -i --exec git show HEAD"' '
+	set_fake_editor &&
 	git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
 	(
 		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -776,6 +823,7 @@
 
 test_expect_success 'running "git rebase --exec git show HEAD -i"' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
 	(
 		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -789,6 +837,7 @@
 
 test_expect_success 'running "git rebase -ix git show HEAD"' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	git rebase -ix "git show HEAD" HEAD~2 >actual &&
 	(
 		FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -802,6 +851,7 @@
 
 test_expect_success 'rebase -ix with several <CMD>' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
 	(
 		FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
@@ -815,6 +865,7 @@
 
 test_expect_success 'rebase -ix with several instances of --exec' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
 	(
 		FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
@@ -836,6 +887,7 @@
 	echo bis >bis.txt &&
 	git add bis.txt &&
 	git commit -m "fixup! two_exec" &&
+	set_fake_editor &&
 	(
 		git checkout -b autosquash_actual &&
 		git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
@@ -854,6 +906,7 @@
 
 test_expect_success 'rebase --exec without -i shows error message' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
 	echo "The --exec option must be used with the --interactive option" >expected &&
 	test_i18ncmp expected actual
@@ -862,6 +915,7 @@
 
 test_expect_success 'rebase -i --exec without <CMD>' '
 	git reset --hard execute &&
+	set_fake_editor &&
 	test_must_fail git rebase -i --exec 2>tmp &&
 	sed -e "1d" tmp >actual &&
 	test_must_fail git rebase -h >expected &&
@@ -871,6 +925,7 @@
 
 test_expect_success 'rebase -i --root re-order and drop commits' '
 	git checkout E &&
+	set_fake_editor &&
 	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) &&
@@ -884,6 +939,7 @@
 	echo B >file7 &&
 	git add file7 &&
 	GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+	set_fake_editor &&
 	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$"
@@ -892,6 +948,7 @@
 test_expect_success 'rebase -i --root temporary sentinel commit' '
 	git checkout B &&
 	(
+		set_fake_editor &&
 		FAKE_LINES="2" &&
 		export FAKE_LINES &&
 		test_must_fail git rebase -i --root
@@ -902,6 +959,7 @@
 
 test_expect_success 'rebase -i --root fixup root commit' '
 	git checkout B &&
+	set_fake_editor &&
 	FAKE_LINES="1 fixup 2" git rebase -i --root &&
 	test A = $(git cat-file commit HEAD | sed -ne \$p) &&
 	test B = $(git show HEAD:file1) &&
@@ -911,6 +969,7 @@
 test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' '
 	git reset --hard &&
 	git checkout conflict-branch &&
+	set_fake_editor &&
 	test_must_fail git rebase --onto HEAD~2 HEAD~ &&
 	test_must_fail git rebase --edit-todo &&
 	git rebase --abort
@@ -919,6 +978,7 @@
 test_expect_success 'rebase --edit-todo can be used to modify todo' '
 	git reset --hard &&
 	git checkout no-conflict-branch^0 &&
+	set_fake_editor &&
 	FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
 	FAKE_LINES="2 1" git rebase --edit-todo &&
 	git rebase --continue
@@ -929,6 +989,7 @@
 test_expect_success 'rebase -i produces readable reflog' '
 	git reset --hard &&
 	git branch -f branch-reflog-test H &&
+	set_fake_editor &&
 	git rebase -i --onto I F branch-reflog-test &&
 	cat >expect <<-\EOF &&
 	rebase -i (start): checkout I
@@ -989,4 +1050,28 @@
 	test_expect_code 1 grep  "	emp" error
 '
 
+test_expect_success 'short SHA-1 setup' '
+	test_when_finished "git checkout master" &&
+	git checkout --orphan collide &&
+	git rm -rf . &&
+	(
+	unset test_tick &&
+	test_commit collide1 collide &&
+	test_commit --notick collide2 collide &&
+	test_commit --notick collide3 collide
+	)
+'
+
+test_expect_success 'short SHA-1 collide' '
+	test_when_finished "reset_rebase && git checkout master" &&
+	git checkout collide &&
+	(
+	unset test_tick &&
+	test_tick &&
+	set_fake_editor &&
+	FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+	FAKE_LINES="reword 1 2" git rebase -i HEAD~2
+	)
+'
+
 test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index 2e0c364..8c251c5 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -28,6 +28,8 @@
 #     \--A3    <-- topic2
 #      \
 #       B2     <-- origin/topic
+#
+# Clone 4 (same as Clone 3)
 
 test_expect_success 'setup for merge-preserving rebase' \
 	'echo First > A &&
@@ -64,6 +66,16 @@
 		git merge --no-ff topic2
 	) &&
 
+	git clone ./. clone4 &&
+	(
+		cd clone4 &&
+		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 &&
 	git commit -a -m "Modify B2"
@@ -96,4 +108,15 @@
 	)
 '
 
+test_expect_success 'rebase -p ignores merge.log config' '
+	(
+	cd clone4 &&
+	git fetch &&
+	git -c merge.log=1 rebase -p origin/topic &&
+	echo >expected &&
+	git log --format="%b" -1 >current &&
+	test_cmp expected current
+	)
+'
+
 test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 6f489e2..bff6ffe 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -100,7 +100,7 @@
 
 '
 
-test_expect_success 'chery-pick on unborn branch' '
+test_expect_success 'cherry-pick on unborn branch' '
 	git checkout --orphan unborn &&
 	git rm --cached -r . &&
 	rm -rf * &&
@@ -109,4 +109,24 @@
 	! test_cmp_rev initial HEAD
 '
 
+test_expect_success 'cherry-pick "-" to pick from previous branch' '
+	git checkout unborn &&
+	test_commit to-pick actual content &&
+	git checkout master &&
+	git cherry-pick - &&
+	echo content >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick "-" is meaningless without checkout' '
+	test_create_repo afresh &&
+	(
+		cd afresh &&
+		test_commit one &&
+		test_commit two &&
+		test_commit three &&
+		test_must_fail git cherry-pick -
+	)
+'
+
 test_done
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index 373aad6..fb889ac 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -105,7 +105,7 @@
 	test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
 '
 
-test_expect_success 'chery-pick --ff on unborn branch' '
+test_expect_success 'cherry-pick --ff on unborn branch' '
 	git checkout --orphan unborn &&
 	git rm --cached -r . &&
 	rm -rf * &&
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
index a5b6a5f..1e5b394 100755
--- a/t/t3509-cherry-pick-merge-df.sh
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -74,7 +74,7 @@
 	echo content > sub/file &&
 	echo foo > othersub/whatever &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	git rm -rf othersub &&
 	git mv sub/file othersub &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 9fab25c..9dc91d0 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -4,18 +4,24 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
-test_expect_success PERL 'setup (initial)' '
+if ! test_have_prereq PERL
+then
+	skip_all='skipping add -i tests, perl not available'
+	test_done
+fi
+
+test_expect_success 'setup (initial)' '
 	echo content >file &&
 	git add file &&
 	echo more >>file &&
 	echo lines >>file
 '
-test_expect_success PERL 'status works (initial)' '
+test_expect_success 'status works (initial)' '
 	git add -i </dev/null >output &&
 	grep "+1/-0 *+2/-0 file" output
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 new file mode 100644
 index 0000000..d95f3ad
@@ -26,19 +32,19 @@
 EOF
 '
 
-test_expect_success PERL 'diff works (initial)' '
+test_expect_success '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 PERL 'revert works (initial)' '
+test_expect_success 'revert works (initial)' '
 	git add file &&
 	(echo r; echo 1) | git add -i &&
 	git ls-files >output &&
 	! grep . output
 '
 
-test_expect_success PERL 'setup (commit)' '
+test_expect_success 'setup (commit)' '
 	echo baseline >file &&
 	git add file &&
 	git commit -m commit &&
@@ -47,12 +53,12 @@
 	echo more >>file &&
 	echo lines >>file
 '
-test_expect_success PERL 'status works (commit)' '
+test_expect_success 'status works (commit)' '
 	git add -i </dev/null >output &&
 	grep "+1/-0 *+2/-0 file" output
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -63,12 +69,12 @@
 EOF
 '
 
-test_expect_success PERL 'diff works (commit)' '
+test_expect_success '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 PERL 'revert works (commit)' '
+test_expect_success 'revert works (commit)' '
 	git add file &&
 	(echo r; echo 1) | git add -i &&
 	git add -i </dev/null >output &&
@@ -76,24 +82,24 @@
 '
 
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 EOF
 '
 
-test_expect_success PERL 'setup fake editor' '
+test_expect_success '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' '
+test_expect_success 'dummy edit works' '
 	(echo e; echo a) | git add -p &&
 	git diff > diff &&
 	test_cmp expected diff
 '
 
-test_expect_success PERL 'setup patch' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 @@ -1,1 +1,4 @@
  this
@@ -103,7 +109,7 @@
 EOF
 '
 
-test_expect_success PERL 'setup fake editor' '
+test_expect_success 'setup fake editor' '
 	echo "#!$SHELL_PATH" >fake_editor.sh &&
 	cat >>fake_editor.sh <<\EOF &&
 mv -f "$1" oldpatch &&
@@ -113,26 +119,26 @@
 	test_set_editor "$(pwd)/fake_editor.sh"
 '
 
-test_expect_success PERL 'bad edit rejected' '
+test_expect_success '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' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 this patch
 is garbage
 EOF
 '
 
-test_expect_success PERL 'garbage edit rejected' '
+test_expect_success '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' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 @@ -1,0 +1,0 @@
  baseline
@@ -142,7 +148,7 @@
 EOF
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b5dd6c9..f910ae9 100644
@@ -157,13 +163,13 @@
 EOF
 '
 
-test_expect_success PERL 'real edit works' '
+test_expect_success 'real edit works' '
 	(echo e; echo n; echo d) | git add -p &&
 	git diff >output &&
 	test_cmp expected output
 '
 
-test_expect_success PERL 'skip files similarly as commit -a' '
+test_expect_success 'skip files similarly as commit -a' '
 	git reset &&
 	echo file >.gitignore &&
 	echo changed >file &&
@@ -177,7 +183,7 @@
 '
 rm -f .gitignore
 
-test_expect_success PERL,FILEMODE 'patch does not affect mode' '
+test_expect_success FILEMODE 'patch does not affect mode' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -186,7 +192,7 @@
 	git diff file | grep "new mode"
 '
 
-test_expect_success PERL,FILEMODE 'stage mode but not hunk' '
+test_expect_success FILEMODE 'stage mode but not hunk' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -196,7 +202,7 @@
 '
 
 
-test_expect_success PERL,FILEMODE 'stage mode and hunk' '
+test_expect_success FILEMODE 'stage mode and hunk' '
 	git reset --hard &&
 	echo content >>file &&
 	chmod +x file &&
@@ -208,14 +214,14 @@
 
 # end of tests disabled when filemode is not usable
 
-test_expect_success PERL 'setup again' '
+test_expect_success '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' '
+test_expect_success 'setup patch' '
 cat >patch <<EOF
 index 180b47c..b6f2c08 100644
 --- a/file
@@ -229,7 +235,7 @@
 '
 
 # Expected output, similar to the patch but w/ diff at the top
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/file b/file
 index b6f2c08..61b9053 100755
@@ -244,7 +250,7 @@
 '
 
 # Test splitting the first patch, then adding both
-test_expect_success PERL 'add first line works' '
+test_expect_success 'add first line works' '
 	git commit -am "clear local changes" &&
 	git apply patch &&
 	(echo s; echo y; echo y) | git add -p file &&
@@ -252,7 +258,7 @@
 	test_cmp expected diff
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/non-empty b/non-empty
 deleted file mode 100644
@@ -264,7 +270,7 @@
 EOF
 '
 
-test_expect_success PERL 'deleting a non-empty file' '
+test_expect_success 'deleting a non-empty file' '
 	git reset --hard &&
 	echo content >non-empty &&
 	git add non-empty &&
@@ -275,7 +281,7 @@
 	test_cmp expected diff
 '
 
-test_expect_success PERL 'setup expected' '
+test_expect_success 'setup expected' '
 cat >expected <<EOF
 diff --git a/empty b/empty
 deleted file mode 100644
@@ -283,7 +289,7 @@
 EOF
 '
 
-test_expect_success PERL 'deleting an empty file' '
+test_expect_success 'deleting an empty file' '
 	git reset --hard &&
 	> empty &&
 	git add empty &&
@@ -294,7 +300,7 @@
 	test_cmp expected diff
 '
 
-test_expect_success PERL 'split hunk setup' '
+test_expect_success 'split hunk setup' '
 	git reset --hard &&
 	for i in 10 20 30 40 50 60
 	do
@@ -310,7 +316,7 @@
 	done >test
 '
 
-test_expect_success PERL 'split hunk "add -p (edit)"' '
+test_expect_success 'split hunk "add -p (edit)"' '
 	# Split, say Edit and do nothing.  Then:
 	#
 	# 1. Broken version results in a patch that does not apply and
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
index 5fe57c5..e4ba601 100755
--- a/t/t3910-mac-os-precompose.sh
+++ b/t/t3910-mac-os-precompose.sh
@@ -36,7 +36,7 @@
 
 test_expect_success "detect if nfd needed" '
 	precomposeunicode=`git config core.precomposeunicode` &&
-	test "$precomposeunicode" = false &&
+	test "$precomposeunicode" = true &&
 	git config core.precomposeunicode true
 '
 test_expect_success "setup" '
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 97172b4..cd04543 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -73,7 +73,7 @@
 test_expect_success 'non-integer config parsing' '
 	git config diff.context no &&
 	test_must_fail git diff 2>output &&
-	test_i18ngrep "bad config value" output
+	test_i18ngrep "bad numeric config value" output
 '
 
 test_expect_success 'negative integer config parsing' '
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 5493500..4286699 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -172,4 +172,20 @@
 	git shortlog HEAD~2.. > out &&
 test_cmp expect out'
 
+test_expect_success 'shortlog ignores commits with missing authors' '
+	git commit --allow-empty -m normal &&
+	git commit --allow-empty -m soon-to-be-broken &&
+	git cat-file commit HEAD >commit.tmp &&
+	sed "/^author/d" commit.tmp >broken.tmp &&
+	commit=$(git hash-object -w -t commit --stdin <broken.tmp) &&
+	git update-ref HEAD $commit &&
+	cat >expect <<-\EOF &&
+	A U Thor (1):
+	      normal
+
+	EOF
+	git shortlog HEAD~2.. >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index ce3eace..0dd8b65 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -484,4 +484,15 @@
 	test_cmp expect actual.fuzz
 '
 
+cat >expect <<\EOF
+Some Dude <some@dude.xx>
+EOF
+
+test_expect_success 'commit --author honors mailmap' '
+	test_must_fail git commit --author "nick" --allow-empty -meight &&
+	git commit --author "Some Dude" --allow-empty -meight &&
+	git show --pretty=format:"%an <%ae>%n" >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh
new file mode 100755
index 0000000..9c5a876
--- /dev/null
+++ b/t/t5308-pack-detect-duplicates.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='handling of duplicate objects in incoming packfiles'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# The sha1s we have in our pack. It's important that these have the same
+# starting byte, so that they end up in the same fanout section of the index.
+# That lets us make sure we are exercising the binary search with both sets.
+LO_SHA1=e68fe8129b546b101aee9510c5328e7f21ca1d18
+HI_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+# And here's a "missing sha1" which will produce failed lookups. It must also
+# be in the same fanout section, and should be between the two (so that during
+# our binary search, we are sure to end up looking at one or the other of the
+# duplicate runs).
+MISSING_SHA1='e69d000000000000000000000000000000000000'
+
+# git will never intentionally create packfiles with
+# duplicate objects, so we have to construct them by hand.
+#
+# $1 is the name of the packfile to create
+#
+# $2 is the number of times to duplicate each object
+create_pack () {
+	pack_header "$((2 * $2))" >"$1" &&
+	for i in $(test_seq 1 "$2"); do
+		pack_obj $LO_SHA1 &&
+		pack_obj $HI_SHA1
+	done >>"$1" &&
+	pack_trailer "$1"
+}
+
+# double-check that create_pack actually works
+test_expect_success 'pack with no duplicates' '
+	create_pack no-dups.pack 1 &&
+	git index-pack --stdin <no-dups.pack
+'
+
+test_expect_success 'index-pack will allow duplicate objects by default' '
+	clear_packs &&
+	create_pack dups.pack 100 &&
+	git index-pack --stdin <dups.pack
+'
+
+test_expect_success 'create batch-check test vectors' '
+	cat >input <<-EOF &&
+	$LO_SHA1
+	$HI_SHA1
+	$MISSING_SHA1
+	EOF
+	cat >expect <<-EOF
+	$LO_SHA1 blob 2
+	$HI_SHA1 blob 0
+	$MISSING_SHA1 missing
+	EOF
+'
+
+test_expect_success 'lookup in duplicated pack (binary search)' '
+	git cat-file --batch-check <input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' '
+	(
+		GIT_USE_LOOKUP=1 &&
+		export GIT_USE_LOOKUP &&
+		git cat-file --batch-check <input >actual
+	) &&
+	test_cmp expect actual
+'
+
+test_expect_success 'index-pack can reject packs with duplicates' '
+	clear_packs &&
+	create_pack dups.pack 2 &&
+	test_must_fail git index-pack --strict --stdin <dups.pack &&
+	test_expect_code 1 git cat-file -e $LO_SHA1
+'
+
+test_done
diff --git a/t/t5309-pack-delta-cycles.sh b/t/t5309-pack-delta-cycles.sh
new file mode 100755
index 0000000..3e7861b
--- /dev/null
+++ b/t/t5309-pack-delta-cycles.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test index-pack handling of delta cycles in packfiles'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# Two similar-ish objects that we have computed deltas between.
+A=01d7713666f4de822776c7622c10f1b07de280dc
+B=e68fe8129b546b101aee9510c5328e7f21ca1d18
+
+# double-check our hand-constucted packs
+test_expect_success 'index-pack works with a single delta (A->B)' '
+	clear_packs &&
+	{
+		pack_header 2 &&
+		pack_obj $A $B &&
+		pack_obj $B
+	} >ab.pack &&
+	pack_trailer ab.pack &&
+	git index-pack --stdin <ab.pack &&
+	git cat-file -t $A &&
+	git cat-file -t $B
+'
+
+test_expect_success 'index-pack works with a single delta (B->A)' '
+	clear_packs &&
+	{
+		pack_header 2 &&
+		pack_obj $A &&
+		pack_obj $B $A
+	} >ba.pack &&
+	pack_trailer ba.pack &&
+	git index-pack --stdin <ba.pack &&
+	git cat-file -t $A &&
+	git cat-file -t $B
+'
+
+test_expect_success 'index-pack detects missing base objects' '
+	clear_packs &&
+	{
+		pack_header 1 &&
+		pack_obj $A $B
+	} >missing.pack &&
+	pack_trailer missing.pack &&
+	test_must_fail git index-pack --fix-thin --stdin <missing.pack
+'
+
+test_expect_success 'index-pack detects REF_DELTA cycles' '
+	clear_packs &&
+	{
+		pack_header 2 &&
+		pack_obj $A $B &&
+		pack_obj $B $A
+	} >cycle.pack &&
+	pack_trailer cycle.pack &&
+	test_must_fail git index-pack --fix-thin --stdin <cycle.pack
+'
+
+test_expect_failure 'failover to an object in another pack' '
+	clear_packs &&
+	git index-pack --stdin <ab.pack &&
+	git index-pack --stdin --fix-thin <cycle.pack
+'
+
+test_expect_failure 'failover to a duplicate object in the same pack' '
+	clear_packs &&
+	{
+		pack_header 3 &&
+		pack_obj $A $B &&
+		pack_obj $B $A &&
+		pack_obj $A
+	} >recoverable.pack &&
+	pack_trailer recoverable.pack &&
+	git index-pack --fix-thin --stdin <recoverable.pack
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index a80584e..d87ddf7 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -393,6 +393,17 @@
 		git fsck --no-dangling
 	)
 '
+test_expect_success 'fetch creating new shallow root' '
+	(
+		git clone "file://$(pwd)/." shallow10 &&
+		git commit --allow-empty -m empty &&
+		cd shallow10 &&
+		git fetch --depth=1 --progress 2>actual &&
+		# This should fetch only the empty commit, no tree or
+		# blob objects
+		grep "remote: Total 1" actual
+	)
+'
 
 test_expect_success 'setup tests for the --stdin parameter' '
 	for head in C D E F
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4691d51..99c32d7 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1172,4 +1172,21 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'push --no-thin must produce non-thin pack' '
+	cat >>path1 <<\EOF &&
+keep base version of path1 big enough, compared to the new changes
+later, in order to pass size heuristics in
+builtin/pack-objects.c:try_delta()
+EOF
+	git commit -am initial &&
+	git init no-thin &&
+	git --git-dir=no-thin/.git config receive.unpacklimit 0 &&
+	git push no-thin/.git refs/heads/master:refs/heads/foo &&
+	echo modified >> path1 &&
+	git commit -am modified &&
+	git repack -adf &&
+	rcvpck="git receive-pack --reject-thin-pack-for-testing" &&
+	git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
+'
+
 test_done
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index ed4d9c8..227d293 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -148,6 +148,95 @@
 	test new = $(git show HEAD:file2)
 '
 
+# add a feature branch, keep-merge, that is merged into master, so the
+# test can try preserving the merge commit (or not) with various
+# --rebase flags/pull.rebase settings.
+test_expect_success 'preserve merge setup' '
+	git reset --hard before-rebase &&
+	git checkout -b keep-merge second^ &&
+	test_commit file3 &&
+	git checkout to-rebase &&
+	git merge keep-merge &&
+	git tag before-preserve-rebase
+'
+
+test_expect_success 'pull.rebase=false create a new merge commit' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase false &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=true flattens keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase true &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase 1 &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase preserve &&
+	git pull . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
+'
+
+test_expect_success 'pull.rebase=invalid fails' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase invalid &&
+	! git pull . copy
+'
+
+test_expect_success '--rebase=false create a new merge commit' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase true &&
+	git pull --rebase=false . copy &&
+	test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success '--rebase=true rebases and flattens keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase preserve &&
+	git pull --rebase=true . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
+test_expect_success '--rebase=preserve rebases and merges keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase true &&
+	git pull --rebase=preserve . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
+'
+
+test_expect_success '--rebase=invalid fails' '
+	git reset --hard before-preserve-rebase &&
+	! git pull --rebase=invalid . copy
+'
+
+test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
+	git reset --hard before-preserve-rebase &&
+	test_config pull.rebase preserve &&
+	git pull --rebase . copy &&
+	test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
+	test file3 = $(git show HEAD:file3.t)
+'
+
 test_expect_success '--rebase with rebased upstream' '
 
 	git remote add -f me . &&
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index c983d36..3932e79 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -54,9 +54,6 @@
 	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
 '
 
diff --git a/t/t5800-remote-testpy.sh b/t/t5800-remote-testpy.sh
deleted file mode 100755
index 1e683d4..0000000
--- a/t/t5800-remote-testpy.sh
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2010 Sverre Rabbelier
-#
-
-test_description='Test python remote-helper framework'
-
-. ./test-lib.sh
-
-if ! test_have_prereq PYTHON ; then
-	skip_all='skipping python remote-helper tests, python not available'
-	test_done
-fi
-
-"$PYTHON_PATH" -c '
-import sys
-if sys.hexversion < 0x02040000:
-    sys.exit(1)
-' || {
-	skip_all='skipping python remote-helper 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 "testpy::${PWD}/server" localclone &&
-	test_cmp public/file localclone/file
-'
-
-test_expect_success 'cloning from remote repo' '
-	git clone "testpy::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-testpy, 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 -R server server2 &&
-	git clone "testpy::${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_expect_success 'proper failure checks for fetching' '
-	(GIT_REMOTE_TESTGIT_FAILURE=1 &&
-	export GIT_REMOTE_TESTGIT_FAILURE &&
-	cd localclone &&
-	test_must_fail git fetch 2>&1 | \
-		grep "Error while running fast-import"
-	)
-'
-
-# We sleep to give fast-export a chance to catch the SIGPIPE
-test_expect_failure 'proper failure checks for pushing' '
-	(GIT_REMOTE_TESTGIT_FAILURE=1 &&
-	export GIT_REMOTE_TESTGIT_FAILURE &&
-	GIT_REMOTE_TESTGIT_SLEEPY=1 &&
-	export GIT_REMOTE_TESTGIT_SLEEPY &&
-	cd localclone &&
-	test_must_fail git push --all 2>&1 | \
-		grep "Error while running fast-export"
-	)
-'
-
-test_done
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 8c4c539..613f69a 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -182,6 +182,17 @@
 	)
 '
 
+test_expect_success 'push update refs disabled by no-private-update' '
+	(cd local &&
+	echo more-update >>file &&
+	git commit -a -m more-update &&
+	git rev-parse --verify testgit/origin/heads/update >expect &&
+	GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE=t git push origin update &&
+	git rev-parse --verify testgit/origin/heads/update >actual &&
+	test_cmp expect actual
+	)
+'
+
 test_expect_success 'push update refs failure' '
 	(cd local &&
 	git checkout update &&
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index c680f78..a89dfbe 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -259,7 +259,7 @@
 	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" &&
+	git commit -m "Common commit" &&
 
 	echo 11 >>sub/file &&
 	echo more >>dir/file-in-the-way &&
@@ -439,7 +439,7 @@
 	mkdir one &&
 	echo stuff >one/file &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	git mv one/file destdir &&
 	git commit -m "Renamed to destdir" &&
@@ -479,7 +479,7 @@
 	echo stuff >one/file &&
 	echo other >two/file &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	git rm -rf one &&
 	git mv two/file one &&
@@ -539,7 +539,7 @@
 
 	echo stuff >original &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	mkdir two &&
 	>two/file &&
@@ -583,7 +583,7 @@
 	mkdir one two &&
 	touch one/file two/file &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	git rm -rf one &&
 	git mv original one &&
@@ -618,7 +618,7 @@
 
 	printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original &&
 	git add -A &&
-	git commit -m "Common commmit" &&
+	git commit -m "Common commit" &&
 
 	git mv original rename &&
 	echo 11 >>rename &&
@@ -649,7 +649,7 @@
 	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 commit -m "Common commit" &&
 
 	git mv df/file temp &&
 	rm -rf df &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index ec2b516..ba26cfe 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -28,10 +28,15 @@
 		git reset --hard HEAD^ &&
 		git checkout -b b4 origin &&
 		advance e &&
-		advance f
+		advance f &&
+		git checkout -b brokenbase origin &&
+		git checkout -b b5 --track brokenbase &&
+		advance g &&
+		git branch -d brokenbase &&
+		git checkout -b b6 origin
 	) &&
 	git checkout -b follower --track master &&
-	advance g
+	advance h
 '
 
 script='s/^..\(b.\)[	 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
@@ -56,6 +61,8 @@
 b2 origin/master: ahead 1, behind 1
 b3 origin/master: behind 1
 b4 origin/master: ahead 2
+b5 brokenbase: gone
+b6 origin/master
 EOF
 
 test_expect_success 'branch -vv' '
@@ -67,7 +74,7 @@
 	test_i18ncmp expect actual
 '
 
-test_expect_success 'checkout' '
+test_expect_success 'checkout (diverged from upstream)' '
 	(
 		cd test && git checkout b1
 	) >actual &&
@@ -80,7 +87,22 @@
 	test_i18ngrep "is ahead of" actual
 '
 
-test_expect_success 'status' '
+test_expect_success 'checkout (upstream is gone)' '
+	(
+		cd test &&
+		git checkout b5
+	) >actual &&
+	test_i18ngrep "is based on .*, but the upstream is gone." actual
+'
+
+test_expect_success 'checkout (up-to-date with upstream)' '
+	(
+		cd test && git checkout b6
+	) >actual &&
+	test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+'
+
+test_expect_success 'status (diverged from upstream)' '
 	(
 		cd test &&
 		git checkout b1 >/dev/null &&
@@ -90,6 +112,65 @@
 	test_i18ngrep "have 1 and 1 different" actual
 '
 
+test_expect_success 'status (upstream is gone)' '
+	(
+		cd test &&
+		git checkout b5 >/dev/null &&
+		# reports nothing to commit
+		test_must_fail git commit --dry-run
+	) >actual &&
+	test_i18ngrep "is based on .*, but the upstream is gone." actual
+'
+
+test_expect_success 'status (up-to-date with upstream)' '
+	(
+		cd test &&
+		git checkout b6 >/dev/null &&
+		# reports nothing to commit
+		test_must_fail git commit --dry-run
+	) >actual &&
+	test_i18ngrep "Your branch is up-to-date with .origin/master" actual
+'
+
+cat >expect <<\EOF
+## b1...origin/master [ahead 1, behind 1]
+EOF
+
+test_expect_success 'status -s -b (diverged from upstream)' '
+	(
+		cd test &&
+		git checkout b1 >/dev/null &&
+		git status -s -b | head -1
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+## b5...brokenbase [gone]
+EOF
+
+test_expect_success 'status -s -b (upstream is gone)' '
+	(
+		cd test &&
+		git checkout b5 >/dev/null &&
+		git status -s -b | head -1
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
+cat >expect <<\EOF
+## b6...origin/master
+EOF
+
+test_expect_success 'status -s -b (up-to-date with upstream)' '
+	(
+		cd test &&
+		git checkout b6 >/dev/null &&
+		git status -s -b | head -1
+	) >actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'fail to track lightweight tags' '
 	git checkout master &&
 	git tag light &&
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index e673c25..7ea14ce 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,39 +6,86 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
-date >path0
-git update-index --add path0
-save_tag tree git write-tree
-hide_error save_tag start unique_commit "start" tree
-save_tag second unique_commit "second" tree -p start
-hide_error save_tag start2 unique_commit "start2" tree
-save_tag two_parents unique_commit "next" tree -p second -p start2
-save_tag final unique_commit "final" tree -p two_parents
+test_cmp_rev_output () {
+	git rev-parse --verify "$1" >expect &&
+	eval "$2" >actual &&
+	test_cmp expect actual
+}
 
-test_expect_success 'start is valid' 'git rev-parse start | grep "^[0-9a-f]\{40\}$"'
-test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git rev-parse start^0)"
-test_expect_success 'start^1 not valid' "if git rev-parse --verify start^1; then false; else :; fi"
-test_expect_success 'second^1 = second^' "test $(git rev-parse second^1) = $(git rev-parse second^)"
-test_expect_success 'final^1^1^1' "test $(git rev-parse start) = $(git rev-parse final^1^1^1)"
-test_expect_success 'final^1^1^1 = final^^^' "test $(git rev-parse final^1^1^1) = $(git rev-parse final^^^)"
-test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
-test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
-test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
-test_expect_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1'
-test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
-test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\""
-test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\""
+test_expect_success 'setup' '
+	test_commit start &&
+	test_commit second &&
+	git checkout --orphan tmp &&
+	test_commit start2 &&
+	git checkout master &&
+	git merge -m next start2 &&
+	test_commit final
+'
 
-test_expect_success 'repack for next test' 'git repack -a -d'
+test_expect_success 'start is valid' '
+	git rev-parse start | grep "^[0-9a-f]\{40\}$"
+'
+
+test_expect_success 'start^0' '
+	test_cmp_rev_output tags/start "git rev-parse start^0"
+'
+
+test_expect_success 'start^1 not valid' '
+	test_must_fail git rev-parse --verify start^1
+'
+
+test_expect_success 'second^1 = second^' '
+	test_cmp_rev_output second^ "git rev-parse second^1"
+'
+
+test_expect_success 'final^1^1^1' '
+	test_cmp_rev_output start "git rev-parse final^1^1^1"
+'
+
+test_expect_success 'final^1^1^1 = final^^^' '
+	test_cmp_rev_output final^^^ "git rev-parse final^1^1^1"
+'
+
+test_expect_success 'final^1^2' '
+	test_cmp_rev_output start2 "git rev-parse final^1^2"
+'
+
+test_expect_success 'final^1^2 != final^1^1' '
+	test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)
+'
+
+test_expect_success 'final^1^3 not valid' '
+	test_must_fail git rev-parse --verify final^1^3
+'
+
+test_expect_success '--verify start2^1' '
+	test_must_fail git rev-parse --verify start2^1
+'
+
+test_expect_success '--verify start2^0' '
+	git rev-parse --verify start2^0
+'
+
+test_expect_success 'final^1^@ = final^1^1 final^1^2' '
+	git rev-parse final^1^1 final^1^2 >expect &&
+	git rev-parse final^1^@ >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
+	git rev-parse final^1 ^final^1^1 ^final^1^2 >expect &&
+	git rev-parse final^1^! >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'repack for next test' '
+	git repack -a -d
+'
+
 test_expect_success 'short SHA-1 works' '
-	start=`git rev-parse --verify start` &&
-	echo $start &&
-	abbrv=`echo $start | sed s/.\$//` &&
-	echo $abbrv &&
-	abbrv=`git rev-parse --verify $abbrv` &&
-	echo $abbrv &&
-	test $start = $abbrv'
+	start=$(git rev-parse --verify start) &&
+	test_cmp_rev_output start "git rev-parse ${start%?}"
+'
 
 test_done
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
new file mode 100755
index 0000000..a997f7a
--- /dev/null
+++ b/t/t7009-filter-branch-null-sha1.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='filter-branch removal of trees with null sha1'
+. ./test-lib.sh
+
+test_expect_success 'setup: base commits' '
+	test_commit one &&
+	test_commit two &&
+	test_commit three
+'
+
+test_expect_success 'setup: a commit with a bogus null sha1 in the tree' '
+	{
+		git ls-tree HEAD &&
+		printf "160000 commit $_z40\\tbroken\\n"
+	} >broken-tree
+	echo "add broken entry" >msg &&
+
+	tree=$(git mktree <broken-tree) &&
+	test_tick &&
+	commit=$(git commit-tree $tree -p HEAD <msg) &&
+	git update-ref HEAD "$commit"
+'
+
+# we have to make one more commit on top removing the broken
+# entry, since otherwise our index does not match HEAD (and filter-branch will
+# complain). We could make the index match HEAD, but doing so would involve
+# writing a null sha1 into the index.
+test_expect_success 'setup: bring HEAD and index in sync' '
+	test_tick &&
+	git commit -a -m "back to normal"
+'
+
+test_expect_success 'filter commands are still checked' '
+	test_must_fail git filter-branch \
+		--force --prune-empty \
+		--index-filter "git rm --cached --ignore-unmatch three.t"
+'
+
+test_expect_success 'removing the broken entry works' '
+	echo three >expect &&
+	git filter-branch \
+		--force --prune-empty \
+		--index-filter "git rm --cached --ignore-unmatch broken" &&
+	git log -1 --format=%s >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 52ef06b..7d467c0 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -29,20 +29,19 @@
 	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
-#
+test_expect_success 'M/D conflict does not segfault' '
+	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 &&
@@ -135,19 +134,19 @@
 	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
+	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
 '
@@ -168,20 +167,20 @@
 
 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
+	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
 '
@@ -192,22 +191,22 @@
 	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
+	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 &&
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
index 8062cf5..af00ab4 100755
--- a/t/t7106-reset-unborn-branch.sh
+++ b/t/t7106-reset-unborn-branch.sh
@@ -11,7 +11,10 @@
 test_expect_success 'reset' '
 	git add a b &&
 	git reset &&
-	test "$(git ls-files)" = ""
+
+	>expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'reset HEAD' '
@@ -24,28 +27,42 @@
 	rm .git/index &&
 	git add a b &&
 	git reset a &&
-	test "$(git ls-files)" = "b"
+
+	echo b >expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
-test_expect_success 'reset -p' '
+test_expect_success PERL 'reset -p' '
 	rm .git/index &&
 	git add a &&
-	echo y | git reset -p &&
-	test "$(git ls-files)" = ""
+	echo y >yes &&
+	git reset -p <yes &&
+
+	>expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'reset --soft is a no-op' '
 	rm .git/index &&
 	git add a &&
-	git reset --soft
-	test "$(git ls-files)" = "a"
+	git reset --soft &&
+
+	echo a >expect &&
+	git ls-files >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'reset --hard' '
 	rm .git/index &&
 	git add a &&
+	test_when_finished "echo a >a" &&
 	git reset --hard &&
-	test "$(git ls-files)" = "" &&
+
+	>expect &&
+	git ls-files >actual &&
+	test_cmp expect actual &&
 	test_path_is_missing a
 '
 
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 4192fe0..10f89bd 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -962,7 +962,6 @@
 		git add sub &&
 		git commit -m "init sub"
 	) &&
-	test_config core.precomposeunicode true &&
 	git submodule add ./"$svname" &&
 	git submodule >&2 &&
 	test -n "$(git submodule | grep "$svname")"
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index ac2434c..5a6d6d6 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -265,13 +265,11 @@
 test_expect_success '--for-status' "
 	git submodule summary --for-status HEAD^ >actual &&
 	test_i18ncmp actual - <<EOF
-# Submodule changes to be committed:
-#
-# * sm1 $head6...0000000:
-#
-# * sm2 0000000...$head7 (2):
-#   > Add foo9
-#
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+  > Add foo9
+
 EOF
 "
 
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index b192f93..f0b3305 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -58,7 +58,7 @@
 	 git submodule add ../merging merging &&
 	 test_tick &&
 	 git commit -m "rebasing"
-	)
+	) &&
 	(cd super &&
 	 git submodule add ../none none &&
 	 test_tick &&
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 99ce36f..f04798f 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -53,7 +53,7 @@
 '
 
 test_expect_success 'using invalid commit with -C' '
-	test_must_fail git commit -C bogus
+	test_must_fail git commit --allow-empty -C bogus
 '
 
 test_expect_success 'nothing to commit' '
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index ac3d0fe..d8c531d 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -60,8 +60,13 @@
 	test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
+strip_comments () {
+	tab='	'
+	sed "s/^\# //; s/^\#$//; s/^#$tab/$tab/" <"$1" >"$1".tmp &&
+	rm "$1" && mv "$1".tmp "$1"
+}
+
 test_expect_success 'status --column' '
-	COLUMNS=50 git status --column="column dense" >output &&
 	cat >expect <<\EOF &&
 # On branch master
 # Changes to be committed:
@@ -78,9 +83,17 @@
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
-#	dir1/untracked dir2/untracked untracked
-#	dir2/modified  output
+#	dir1/untracked dir2/untracked output
+#	dir2/modified  expect         untracked
+#
 EOF
+	COLUMNS=50 git -c status.displayCommentPrefix=true status --column="column dense" >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'status --column status.displayCommentPrefix=false' '
+	strip_comments expect &&
+	COLUMNS=49 git -c status.displayCommentPrefix=false status --column="column dense" >output &&
 	test_i18ncmp expect output
 '
 
@@ -106,28 +119,58 @@
 #	expect
 #	output
 #	untracked
+#
 EOF
 
-test_expect_success 'status (2)' '
-	git status >output &&
+test_expect_success 'status with status.displayCommentPrefix=true' '
+	git -c status.displayCommentPrefix=true status >output &&
 	test_i18ncmp expect output
 '
 
+test_expect_success 'status with status.displayCommentPrefix=false' '
+	strip_comments expect &&
+	git -c status.displayCommentPrefix=false status >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'setup fake editor' '
+	cat >.git/editor <<-\EOF &&
+	#! /bin/sh
+	cp "$1" output
+EOF
+	chmod 755 .git/editor
+'
+
+commit_template_commented () {
+	(
+		EDITOR=.git/editor &&
+		export EDITOR &&
+		# Fails due to empty message
+		test_must_fail git commit
+	) &&
+	! grep '^[^#]' output
+}
+
+test_expect_success 'commit ignores status.displayCommentPrefix=false in COMMIT_EDITMSG' '
+	commit_template_commented
+'
+
 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
+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)' '
@@ -185,33 +228,35 @@
 	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
+	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
 '
@@ -246,30 +291,31 @@
 	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
+	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
 '
@@ -310,22 +356,22 @@
 	: >dir3/untracked2
 '
 
-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 not listed (use -u option to show untracked files)
-EOF
 test_expect_success 'status -uno' '
+	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 not listed (use -u option to show untracked files)
+EOF
 	git status -uno >output &&
 	test_i18ncmp expect output
 '
@@ -336,17 +382,17 @@
 	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
 test_expect_success 'status -uno (advice.statusHints false)' '
+	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
 	test_config advice.statusHints false &&
 	git status -uno >output &&
 	test_i18ncmp expect output
@@ -367,31 +413,32 @@
 	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)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	dir3/
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'status -unormal' '
+	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)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	dir3/
+	expect
+	output
+	untracked
+
+EOF
 	git status -unormal >output &&
 	test_i18ncmp expect output
 '
@@ -424,32 +471,33 @@
 	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)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	dir3/untracked1
-#	dir3/untracked2
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'status -uall' '
+	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)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	dir3/untracked1
+	dir3/untracked2
+	expect
+	output
+	untracked
+
+EOF
 	git status -uall >output &&
 	test_i18ncmp expect output
 '
@@ -486,31 +534,31 @@
 	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:   modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#	untracked
-#	../dir2/modified
-#	../dir2/untracked
-#	../expect
-#	../output
-#	../untracked
-EOF
-
 test_expect_success 'status with relative paths' '
+	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:   modified
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+	untracked
+	../dir2/modified
+	../dir2/untracked
+	../expect
+	../output
+	../untracked
+
+EOF
 	(cd dir1 && git status) >output &&
 	test_i18ncmp expect output
 '
@@ -557,31 +605,31 @@
 
 '
 
-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' '
+	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_config color.ui always &&
 	git status | test_decode_color >output &&
 	test_i18ncmp expect output
@@ -685,33 +733,33 @@
 
 '
 
-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)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	expect
-#	output
-#	untracked
-EOF
 
 
 test_expect_success 'status without relative paths' '
+	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)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	expect
+	output
+	untracked
+
+EOF
 	test_config status.relativePaths false &&
 	(cd dir1 && git status) >output &&
 	test_i18ncmp expect output
@@ -737,23 +785,24 @@
 
 '
 
-cat <<EOF >expect
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#	modified:   dir1/modified
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#	dir1/untracked
-#	dir2/
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'dry-run of partial commit excluding new file in index' '
+	cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+	modified:   dir1/modified
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+	dir1/untracked
+	dir2/
+	expect
+	output
+	untracked
+
+EOF
 	git commit --dry-run dir1/modified >output &&
 	test_i18ncmp expect output
 '
@@ -778,31 +827,32 @@
 	git add sm
 '
 
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#	new file:   dir2/added
-#	new file:   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
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'status submodule summary is disabled by default' '
+	cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+	new file:   dir2/added
+	new file:   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
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	expect
+	output
+	untracked
+
+EOF
 	git status >output &&
 	test_i18ncmp expect output
 '
@@ -837,41 +887,52 @@
 
 head=$(cd sm && git rev-parse --short=7 --verify HEAD)
 
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD <file>..." to unstage)
-#
-#	new file:   dir2/added
-#	new file:   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 0000000...$head (1):
-#   > Add foo
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'status submodule summary' '
+	cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+	new file:   dir2/added
+	new file:   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 0000000...$head (1):
+  > Add foo
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	expect
+	output
+	untracked
+
+EOF
 	git config status.submodulesummary 10 &&
 	git status >output &&
 	test_i18ncmp expect output
 '
 
+test_expect_success 'status submodule summary with status.displayCommentPrefix=false' '
+	strip_comments expect &&
+	git -c status.displayCommentPrefix=false status >output &&
+	test_i18ncmp expect output
+'
+
+test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' '
+	commit_template_commented
+'
+
 cat >expect <<EOF
  M dir1/modified
 A  dir2/added
@@ -888,26 +949,27 @@
 	test_cmp expect output
 '
 
-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)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	expect
-#	output
-#	untracked
+test_expect_success 'status submodule summary (clean submodule): commit' '
+	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)
+
+	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 'status submodule summary (clean submodule): commit' '
 	git commit -m "commit submodule" &&
 	git config status.submodulesummary 10 &&
 	test_must_fail git commit --dry-run >output &&
@@ -937,36 +999,37 @@
 	test_cmp expect output
 '
 
-cat >expect <<EOF
-# On branch master
-# Changes to be committed:
-#   (use "git reset HEAD^1 <file>..." to unstage)
-#
-#	new file:   dir2/added
-#	new file:   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 0000000...$head (1):
-#   > Add foo
-#
-# Untracked files:
-#   (use "git add <file>..." to include in what will be committed)
-#
-#	dir1/untracked
-#	dir2/modified
-#	dir2/untracked
-#	expect
-#	output
-#	untracked
-EOF
 test_expect_success 'commit --dry-run submodule summary (--amend)' '
+	cat >expect <<EOF &&
+On branch master
+Changes to be committed:
+  (use "git reset HEAD^1 <file>..." to unstage)
+
+	new file:   dir2/added
+	new file:   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 0000000...$head (1):
+  > Add foo
+
+Untracked files:
+  (use "git add <file>..." to include in what will be committed)
+
+	dir1/untracked
+	dir2/modified
+	dir2/untracked
+	expect
+	output
+	untracked
+
+EOF
 	git config status.submodulesummary 10 &&
 	git commit --dry-run --amend >output &&
 	test_i18ncmp expect output
@@ -991,37 +1054,37 @@
 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' '
+	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
 	echo modified  sm/untracked &&
 	git status --ignore-submodules=untracked >output &&
 	test_i18ncmp expect output
@@ -1101,39 +1164,39 @@
 	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" '
+	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
 	git status --ignore-submodules=untracked > output &&
 	test_i18ncmp expect output
 '
@@ -1159,43 +1222,43 @@
 
 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" '
+	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
 	git status --ignore-submodules=untracked > output &&
 	test_i18ncmp expect output
 '
@@ -1276,42 +1339,43 @@
 ;	expect
 ;	output
 ;	untracked
+;
 EOF
 
 test_expect_success "status (core.commentchar with submodule summary)" '
 	test_config core.commentchar ";" &&
-	git status >output &&
+	git -c status.displayCommentPrefix=true status >output &&
 	test_i18ncmp expect output
 '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
 	test_config core.commentchar ";;" &&
-	git status >output &&
+	git -c status.displayCommentPrefix=true status >output &&
 	test_i18ncmp expect output
 '
 
-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
+test_expect_success "--ignore-submodules=all suppresses submodule summary" '
+	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
 '
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 31a798f..0688d58 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -25,18 +25,18 @@
 
 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
+	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
 '
@@ -47,17 +47,17 @@
 	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
+	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
 '
@@ -76,21 +76,21 @@
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short HEAD^^) &&
 	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -103,18 +103,18 @@
 	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
 	echo three >main.txt &&
 	git add main.txt &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -135,21 +135,21 @@
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short rebase_i_conflicts) &&
 	test_must_fail git rebase -i rebase_i_conflicts &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -161,18 +161,18 @@
 	ONTO=$(git rev-parse --short rebase_i_conflicts) &&
 	test_must_fail git rebase -i rebase_i_conflicts &&
 	git add main.txt &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -189,14 +189,14 @@
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short HEAD~2) &&
 	git rebase -i HEAD~2 &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -215,19 +215,19 @@
 	ONTO=$(git rev-parse --short HEAD~3) &&
 	git rebase -i HEAD~3 &&
 	git reset HEAD^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -246,14 +246,14 @@
 	ONTO=$(git rev-parse --short HEAD~3) &&
 	git rebase -i HEAD~3 &&
 	git commit --amend -m "foo" &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -276,14 +276,14 @@
 	ONTO=$(git rev-parse --short HEAD~3) &&
 	git rebase -i HEAD~3 &&
 	git rebase --continue &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -298,19 +298,19 @@
 	git rebase -i HEAD~3 &&
 	git rebase --continue &&
 	git reset HEAD^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -325,14 +325,14 @@
 	git rebase -i HEAD~3 &&
 	git rebase --continue &&
 	git commit --amend -m "foo" &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -347,14 +347,14 @@
 	git rebase -i HEAD~3 &&
 	git commit --amend -m "a" &&
 	git rebase --continue &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -370,19 +370,19 @@
 	git commit --amend -m "b" &&
 	git rebase --continue &&
 	git reset HEAD^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -398,14 +398,14 @@
 	git commit --amend -m "c" &&
 	git rebase --continue &&
 	git commit --amend -m "d" &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -422,14 +422,14 @@
 	git add main.txt &&
 	git commit -m "e" &&
 	git rebase --continue &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -447,19 +447,19 @@
 	git commit --amend -m "f" &&
 	git rebase --continue &&
 	git reset HEAD^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -477,14 +477,14 @@
 	git commit --amend -m "g" &&
 	git rebase --continue &&
 	git commit --amend -m "h" &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
-	#   (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
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
+  (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
 '
@@ -504,15 +504,15 @@
 	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 --continue")
-	#   (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
+	cat >expected <<\EOF &&
+On branch am_already_exists
+You are in the middle of an am session.
+  (fix conflicts and then run "git am --continue")
+  (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
 '
@@ -526,15 +526,15 @@
 	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 --continue")
-	#   (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
+	cat >expected <<\EOF &&
+On branch am_not_exists
+You are in the middle of an am session.
+  (fix conflicts and then run "git am --continue")
+  (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
 '
@@ -549,15 +549,15 @@
 	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
+	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
 '
@@ -574,13 +574,13 @@
 	git bisect bad &&
 	git bisect good one_bisect &&
 	TGT=$(git rev-parse --short two_bisect) &&
-	cat >expected <<-EOF &&
-	# HEAD detached at $TGT
-	# You are currently bisecting, started from branch '\''bisect'\''.
-	#   (use "git bisect reset" to get back to the original branch)
-	#
-	nothing to commit (use -u to show untracked files)
-	EOF
+	cat >expected <<EOF &&
+HEAD detached at $TGT
+You are currently bisecting, started from branch '\''bisect'\''.
+  (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
 '
@@ -597,15 +597,15 @@
 	test_when_finished "git rebase --abort" &&
 	ONTO=$(git rev-parse --short HEAD^^) &&
 	test_must_fail git rebase HEAD^ --onto HEAD^^ &&
-	cat >expected <<-EOF &&
-	# rebase in progress; onto $ONTO
-	# You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
-	#
-	# Unmerged paths:
-	#	both modified:      main.txt
-	#
-	no changes added to commit
-	EOF
+	cat >expected <<EOF &&
+rebase in progress; onto $ONTO
+You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
+
+Unmerged paths:
+	both modified:      main.txt
+
+no changes added to commit
+EOF
 	git status --untracked-files=no >actual &&
 	test_i18ncmp expected actual
 '
@@ -626,19 +626,19 @@
 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 cherry-pick --continue")
-	#   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
-	#
-	# 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
+	cat >expected <<\EOF &&
+On branch cherry_branch
+You are currently cherry-picking.
+  (fix conflicts and run "git cherry-pick --continue")
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+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
 '
@@ -650,18 +650,18 @@
 	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 cherry-pick --continue")
-	#   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
-	#
-	# Changes to be committed:
-	#
-	#	modified:   main.txt
-	#
-	# Untracked files not listed (use -u option to show untracked files)
-	EOF
+	cat >expected <<\EOF &&
+On branch cherry_branch
+You are currently cherry-picking.
+  (all conflicts fixed: run "git cherry-pick --continue")
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+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
 '
@@ -669,18 +669,18 @@
 test_expect_success 'status showing detached at and from a tag' '
 	test_commit atag tagging &&
 	git checkout atag &&
-	cat >expected <<-\EOF
-	# HEAD detached at atag
-	nothing to commit (use -u to show untracked files)
-	EOF
+	cat >expected <<\EOF
+HEAD detached at atag
+nothing to commit (use -u to show untracked files)
+EOF
 	git status --untracked-files=no >actual &&
 	test_i18ncmp expected actual &&
 
 	git reset --hard HEAD^ &&
-	cat >expected <<-\EOF
-	# HEAD detached from atag
-	nothing to commit (use -u to show untracked files)
-	EOF
+	cat >expected <<\EOF
+HEAD detached from atag
+nothing to commit (use -u to show untracked files)
+EOF
 	git status --untracked-files=no >actual &&
 	test_i18ncmp expected actual
 '
@@ -695,20 +695,20 @@
 	test_commit new to-revert.txt &&
 	TO_REVERT=$(git rev-parse --short HEAD^) &&
 	test_must_fail git revert $TO_REVERT &&
-	cat >expected <<-EOF
-	# On branch master
-	# You are currently reverting commit $TO_REVERT.
-	#   (fix conflicts and run "git revert --continue")
-	#   (use "git revert --abort" to cancel the revert operation)
-	#
-	# Unmerged paths:
-	#   (use "git reset HEAD <file>..." to unstage)
-	#   (use "git add <file>..." to mark resolution)
-	#
-	#	both modified:      to-revert.txt
-	#
-	no changes added to commit (use "git add" and/or "git commit -a")
-	EOF
+	cat >expected <<EOF
+On branch master
+You are currently reverting commit $TO_REVERT.
+  (fix conflicts and run "git revert --continue")
+  (use "git revert --abort" to cancel the revert operation)
+
+Unmerged paths:
+  (use "git reset HEAD <file>..." to unstage)
+  (use "git add <file>..." to mark resolution)
+
+	both modified:      to-revert.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
 '
@@ -716,29 +716,29 @@
 test_expect_success 'status while reverting commit (conflicts resolved)' '
 	echo reverted >to-revert.txt &&
 	git add to-revert.txt &&
-	cat >expected <<-EOF
-	# On branch master
-	# You are currently reverting commit $TO_REVERT.
-	#   (all conflicts fixed: run "git revert --continue")
-	#   (use "git revert --abort" to cancel the revert operation)
-	#
-	# Changes to be committed:
-	#   (use "git reset HEAD <file>..." to unstage)
-	#
-	#	modified:   to-revert.txt
-	#
-	# Untracked files not listed (use -u option to show untracked files)
-	EOF
+	cat >expected <<EOF
+On branch master
+You are currently reverting commit $TO_REVERT.
+  (all conflicts fixed: run "git revert --continue")
+  (use "git revert --abort" to cancel the revert operation)
+
+Changes to be committed:
+  (use "git reset HEAD <file>..." to unstage)
+
+	modified:   to-revert.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 after reverting commit' '
 	git revert --continue &&
-	cat >expected <<-\EOF
-	# On branch master
-	nothing to commit (use -u to show untracked files)
-	EOF
+	cat >expected <<\EOF
+On branch master
+nothing to commit (use -u to show untracked files)
+EOF
 	git status --untracked-files=no >actual &&
 	test_i18ncmp expected actual
 '
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 31a770d..88fc407 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -2864,14 +2864,14 @@
 '
 
 #
-# notemodify, mark in committish
+# notemodify, mark in commit-ish
 #
-test_expect_success 'S: notemodify with garbarge after mark committish must fail' '
+test_expect_success 'S: notemodify with garbarge after mark commit-ish 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 S note commit-ish
 	COMMIT
 	N :202 :302x
 	EOF
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1aa27bd..0fa7dfd 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -700,15 +700,6 @@
 	error "You haven't built things yet, have you?"
 }
 
-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:'
diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample
index 586e3bf..68d62d5 100755
--- a/templates/hooks--pre-commit.sample
+++ b/templates/hooks--pre-commit.sample
@@ -16,7 +16,7 @@
 fi
 
 # If you want to allow non-ASCII filenames set this variable to true.
-allownonascii=$(git config hooks.allownonascii)
+allownonascii=$(git config --bool hooks.allownonascii)
 
 # Redirect output to stderr.
 exec 1>&2
diff --git a/test-match-trees.c b/test-match-trees.c
index a3c4688..2ef725e 100644
--- a/test-match-trees.c
+++ b/test-match-trees.c
@@ -12,10 +12,10 @@
 		die("cannot parse %s as an object name", av[2]);
 	one = parse_tree_indirect(hash1);
 	if (!one)
-		die("not a treeish %s", av[1]);
+		die("not a tree-ish %s", av[1]);
 	two = parse_tree_indirect(hash2);
 	if (!two)
-		die("not a treeish %s", av[2]);
+		die("not a tree-ish %s", av[2]);
 
 	shift_tree(one->object.sha1, two->object.sha1, shifted, -1);
 	printf("shifted: %s\n", sha1_to_hex(shifted));
diff --git a/test-sha1.c b/test-sha1.c
index 80daba9..e57eae1 100644
--- a/test-sha1.c
+++ b/test-sha1.c
@@ -5,10 +5,15 @@
 	git_SHA_CTX ctx;
 	unsigned char sha1[20];
 	unsigned bufsz = 8192;
+	int binary = 0;
 	char *buffer;
 
-	if (ac == 2)
-		bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+	if (ac == 2) {
+		if (!strcmp(av[1], "-b"))
+			binary = 1;
+		else
+			bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+	}
 
 	if (!bufsz)
 		bufsz = 8192;
@@ -42,6 +47,10 @@
 		git_SHA1_Update(&ctx, buffer, this_sz);
 	}
 	git_SHA1_Final(sha1, &ctx);
-	puts(sha1_to_hex(sha1));
+
+	if (binary)
+		fwrite(sha1, 1, 20, stdout);
+	else
+		puts(sha1_to_hex(sha1));
 	exit(0);
 }
diff --git a/transport-helper.c b/transport-helper.c
index 4c2a39e..b32e2d6 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -28,7 +28,8 @@
 		connect : 1,
 		signed_tags : 1,
 		check_connectivity : 1,
-		no_disconnect_req : 1;
+		no_disconnect_req : 1,
+		no_private_update : 1;
 	char *export_marks;
 	char *import_marks;
 	/* These go from remote name (as in "list") to private name */
@@ -208,6 +209,8 @@
 			strbuf_addstr(&arg, "--import-marks=");
 			strbuf_addstr(&arg, capname + strlen("import-marks "));
 			data->import_marks = strbuf_detach(&arg, NULL);
+		} else if (!prefixcmp(capname, "no-private-update")) {
+			data->no_private_update = 1;
 		} else if (mandatory) {
 			die("Unknown mandatory capability %s. This remote "
 			    "helper probably needs newer version of Git.",
@@ -738,7 +741,7 @@
 		if (push_update_ref_status(&buf, &ref, remote_refs))
 			continue;
 
-		if (!data->refspecs)
+		if (!data->refspecs || data->no_private_update)
 			continue;
 
 		/* propagate back the update to the remote namespace */
diff --git a/tree.c b/tree.c
index 549e588..c8c49d7 100644
--- a/tree.c
+++ b/tree.c
@@ -225,6 +225,14 @@
 	return parse_tree_buffer(item, buffer, size);
 }
 
+void free_tree_buffer(struct tree *tree)
+{
+	free(tree->buffer);
+	tree->buffer = NULL;
+	tree->size = 0;
+	tree->object.parsed = 0;
+}
+
 struct tree *parse_tree_indirect(const unsigned char *sha1)
 {
 	struct object *obj = parse_object(sha1);
diff --git a/tree.h b/tree.h
index 9dc90ba..d84ac63 100644
--- a/tree.h
+++ b/tree.h
@@ -16,6 +16,7 @@
 int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
 
 int parse_tree(struct tree *tree);
+void free_tree_buffer(struct tree *tree);
 
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index b03492e..a6c54e0 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -41,6 +41,7 @@
 static struct object_array want_obj;
 static struct object_array extra_edge_obj;
 static unsigned int timeout;
+static int keepalive = 5;
 /* 0 for no sideband,
  * otherwise maximum packet size (up to 65520 bytes).
  */
@@ -69,87 +70,28 @@
 	return sz;
 }
 
-static FILE *pack_pipe = NULL;
-static void show_commit(struct commit *commit, void *data)
-{
-	if (commit->object.flags & BOUNDARY)
-		fputc('-', pack_pipe);
-	if (fputs(sha1_to_hex(commit->object.sha1), pack_pipe) < 0)
-		die("broken output pipe");
-	fputc('\n', pack_pipe);
-	fflush(pack_pipe);
-	free(commit->buffer);
-	commit->buffer = NULL;
-}
-
-static void show_object(struct object *obj,
-			const struct name_path *path, const char *component,
-			void *cb_data)
-{
-	show_object_with_name(pack_pipe, obj, path, component);
-}
-
-static void show_edge(struct commit *commit)
-{
-	fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
-}
-
-static int do_rev_list(int in, int out, void *user_data)
-{
-	int i;
-	struct rev_info revs;
-
-	pack_pipe = xfdopen(out, "w");
-	init_revisions(&revs, NULL);
-	revs.tag_objects = 1;
-	revs.tree_objects = 1;
-	revs.blob_objects = 1;
-	if (use_thin_pack)
-		revs.edge_hint = 1;
-
-	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);
-	return 0;
-}
-
 static void create_pack_file(void)
 {
-	struct async rev_list;
 	struct child_process pack_objects;
 	char data[8193], progress[128];
 	char abort_msg[] = "aborting due to possible repository "
 		"corruption on the remote side.";
 	int buffered = -1;
 	ssize_t sz;
-	const char *argv[10];
-	int arg = 0;
+	const char *argv[12];
+	int i, arg = 0;
+	FILE *pipe_fd;
+	char *shallow_file = NULL;
 
-	argv[arg++] = "pack-objects";
-	if (!shallow_nr) {
-		argv[arg++] = "--revs";
-		if (use_thin_pack)
-			argv[arg++] = "--thin";
+	if (shallow_nr) {
+		shallow_file = setup_temporary_shallow();
+		argv[arg++] = "--shallow-file";
+		argv[arg++] = shallow_file;
 	}
+	argv[arg++] = "pack-objects";
+	argv[arg++] = "--revs";
+	if (use_thin_pack)
+		argv[arg++] = "--thin";
 
 	argv[arg++] = "--stdout";
 	if (!no_progress)
@@ -170,29 +112,21 @@
 	if (start_command(&pack_objects))
 		die("git upload-pack: unable to fork git-pack-objects");
 
-	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");
-		int i;
+	pipe_fd = xfdopen(pack_objects.in, "w");
 
-		for (i = 0; i < want_obj.nr; i++)
-			fprintf(pipe_fd, "%s\n",
-				sha1_to_hex(want_obj.objects[i].item->sha1));
-		fprintf(pipe_fd, "--not\n");
-		for (i = 0; i < have_obj.nr; i++)
-			fprintf(pipe_fd, "%s\n",
-				sha1_to_hex(have_obj.objects[i].item->sha1));
-		fprintf(pipe_fd, "\n");
-		fflush(pipe_fd);
-		fclose(pipe_fd);
-	}
-
+	for (i = 0; i < want_obj.nr; i++)
+		fprintf(pipe_fd, "%s\n",
+			sha1_to_hex(want_obj.objects[i].item->sha1));
+	fprintf(pipe_fd, "--not\n");
+	for (i = 0; i < have_obj.nr; i++)
+		fprintf(pipe_fd, "%s\n",
+			sha1_to_hex(have_obj.objects[i].item->sha1));
+	for (i = 0; i < extra_edge_obj.nr; i++)
+		fprintf(pipe_fd, "%s\n",
+			sha1_to_hex(extra_edge_obj.objects[i].item->sha1));
+	fprintf(pipe_fd, "\n");
+	fflush(pipe_fd);
+	fclose(pipe_fd);
 
 	/* We read from pack_objects.err to capture stderr output for
 	 * progress bar, and pack_objects.out to capture the pack data.
@@ -201,6 +135,7 @@
 	while (1) {
 		struct pollfd pfd[2];
 		int pe, pu, pollsize;
+		int ret;
 
 		reset_timeout();
 
@@ -223,7 +158,8 @@
 		if (!pollsize)
 			break;
 
-		if (poll(pfd, pollsize, -1) < 0) {
+		ret = poll(pfd, pollsize, 1000 * keepalive);
+		if (ret < 0) {
 			if (errno != EINTR) {
 				error("poll failed, resuming: %s",
 				      strerror(errno));
@@ -285,14 +221,32 @@
 			if (sz < 0)
 				goto fail;
 		}
+
+		/*
+		 * We hit the keepalive timeout without saying anything; send
+		 * an empty message on the data sideband just to let the other
+		 * side know we're still working on it, but don't have any data
+		 * yet.
+		 *
+		 * If we don't have a sideband channel, there's no room in the
+		 * protocol to say anything, so those clients are just out of
+		 * luck.
+		 */
+		if (!ret && use_sideband) {
+			static const char buf[] = "0005\1";
+			write_or_die(1, buf, 5);
+		}
 	}
 
 	if (finish_command(&pack_objects)) {
 		error("git upload-pack: git-pack-objects died with error.");
 		goto fail;
 	}
-	if (shallow_nr && finish_async(&rev_list))
-		goto fail;	/* error was already reported */
+	if (shallow_file) {
+		if (*shallow_file)
+			unlink(shallow_file);
+		free(shallow_file);
+	}
 
 	/* flush the data */
 	if (0 <= buffered) {
@@ -786,6 +740,11 @@
 {
 	if (!strcmp("uploadpack.allowtipsha1inwant", var))
 		allow_tip_sha1_in_want = git_config_bool(var, value);
+	else if (!strcmp("uploadpack.keepalive", var)) {
+		keepalive = git_config_int(var, value);
+		if (!keepalive)
+			keepalive = -1;
+	}
 	return parse_hide_refs_config(var, value, "uploadpack");
 }
 
diff --git a/urlmatch.c b/urlmatch.c
index 1db76c8..ec87cba 100644
--- a/urlmatch.c
+++ b/urlmatch.c
@@ -281,9 +281,11 @@
 		url_len--;
 	}
 	for (;;) {
-		const char *seg_start = norm.buf + norm.len;
+		const char *seg_start;
+		size_t seg_start_off = norm.len;
 		const char *next_slash = url + strcspn(url, "/?#");
 		int skip_add_slash = 0;
+
 		/*
 		 * RFC 3689 indicates that any . or .. segments should be
 		 * unescaped before being checked for.
@@ -297,6 +299,8 @@
 			strbuf_release(&norm);
 			return NULL;
 		}
+
+		seg_start = norm.buf + seg_start_off;
 		if (!strcmp(seg_start, ".")) {
 			/* ignore a . segment; be careful not to remove initial '/' */
 			if (seg_start == path_start + 1) {
diff --git a/walker.c b/walker.c
index be389dc..633596e 100644
--- a/walker.c
+++ b/walker.c
@@ -56,10 +56,7 @@
 		if (!obj || process(walker, obj))
 			return -1;
 	}
-	free(tree->buffer);
-	tree->buffer = NULL;
-	tree->size = 0;
-	tree->object.parsed = 0;
+	free_tree_buffer(tree);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index ff4b324..cbdce72 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -9,6 +9,7 @@
 #include "diffcore.h"
 #include "quote.h"
 #include "run-command.h"
+#include "argv-array.h"
 #include "remote.h"
 #include "refs.h"
 #include "submodule.h"
@@ -46,9 +47,11 @@
 
 	strbuf_vaddf(&sb, fmt, ap);
 	if (!sb.len) {
-		strbuf_addch(&sb, comment_line_char);
-		if (!trail)
-			strbuf_addch(&sb, ' ');
+		if (s->display_comment_prefix) {
+			strbuf_addch(&sb, comment_line_char);
+			if (!trail)
+				strbuf_addch(&sb, ' ');
+		}
 		color_print_strbuf(s->fp, color, &sb);
 		if (trail)
 			fprintf(s->fp, "%s", trail);
@@ -59,7 +62,7 @@
 		eol = strchr(line, '\n');
 
 		strbuf_reset(&linebuf);
-		if (at_bol) {
+		if (at_bol && s->display_comment_prefix) {
 			strbuf_addch(&linebuf, comment_line_char);
 			if (*line != '\n' && *line != '\t')
 				strbuf_addch(&linebuf, ' ');
@@ -129,6 +132,7 @@
 	s->untracked.strdup_strings = 1;
 	s->ignored.strdup_strings = 1;
 	s->show_branch = -1;  /* unspecified */
+	s->display_comment_prefix = 0;
 }
 
 static void wt_status_print_unmerged_header(struct wt_status *s)
@@ -161,7 +165,7 @@
 		}
 	}
 
-	if (!advice_status_hints)
+	if (!s->hints)
 		return;
 	if (s->whence != FROM_COMMIT)
 		;
@@ -188,7 +192,7 @@
 	const char *c = color(WT_STATUS_HEADER, s);
 
 	status_printf_ln(s, c, _("Changes to be committed:"));
-	if (!advice_status_hints)
+	if (!s->hints)
 		return;
 	if (s->whence != FROM_COMMIT)
 		; /* NEEDSWORK: use "git reset --unresolve"??? */
@@ -206,7 +210,7 @@
 	const char *c = color(WT_STATUS_HEADER, s);
 
 	status_printf_ln(s, c, _("Changes not staged for commit:"));
-	if (!advice_status_hints)
+	if (!s->hints)
 		return;
 	if (!has_deleted)
 		status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
@@ -224,7 +228,7 @@
 {
 	const char *c = color(WT_STATUS_HEADER, s);
 	status_printf_ln(s, c, "%s:", what);
-	if (!advice_status_hints)
+	if (!s->hints)
 		return;
 	status_printf_ln(s, c, _("  (use \"git %s <file>...\" to include in what will be committed)"), how);
 	status_printf_ln(s, c, "");
@@ -661,29 +665,57 @@
 	char summary_limit[64];
 	char index[PATH_MAX];
 	const char *env[] = { NULL, NULL };
-	const char *argv[8];
-
-	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;
+	struct argv_array argv = ARGV_ARRAY_INIT;
+	struct strbuf cmd_stdout = STRBUF_INIT;
+	struct strbuf summary = STRBUF_INIT;
+	char *summary_content;
+	size_t len;
 
 	sprintf(summary_limit, "%d", s->submodule_summary);
 	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
 
+	env[0] = index;
+	argv_array_push(&argv, "submodule");
+	argv_array_push(&argv, "summary");
+	argv_array_push(&argv, uncommitted ? "--files" : "--cached");
+	argv_array_push(&argv, "--for-status");
+	argv_array_push(&argv, "--summary-limit");
+	argv_array_push(&argv, summary_limit);
+	if (!uncommitted)
+		argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
+
 	memset(&sm_summary, 0, sizeof(sm_summary));
-	sm_summary.argv = argv;
+	sm_summary.argv = argv.argv;
 	sm_summary.env = env;
 	sm_summary.git_cmd = 1;
 	sm_summary.no_stdin = 1;
 	fflush(s->fp);
-	sm_summary.out = dup(fileno(s->fp));    /* run_command closes it */
+	sm_summary.out = -1;
+
 	run_command(&sm_summary);
+	argv_array_clear(&argv);
+
+	len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);
+
+	/* prepend header, only if there's an actual output */
+	if (len) {
+		if (uncommitted)
+			strbuf_addstr(&summary, _("Submodules changed but not updated:"));
+		else
+			strbuf_addstr(&summary, _("Submodule changes to be committed:"));
+		strbuf_addstr(&summary, "\n\n");
+	}
+	strbuf_addbuf(&summary, &cmd_stdout);
+	strbuf_release(&cmd_stdout);
+
+	if (s->display_comment_prefix) {
+		summary_content = strbuf_detach(&summary, &len);
+		strbuf_add_commented_lines(&summary, summary_content, len);
+		free(summary_content);
+	}
+
+	fputs(summary.buf, s->fp);
+	strbuf_release(&summary);
 }
 
 static void wt_status_print_other(struct wt_status *s,
@@ -717,10 +749,11 @@
 
 	strbuf_release(&buf);
 	if (!column_active(s->colopts))
-		return;
+		goto conclude;
 
-	strbuf_addf(&buf, "%s#\t%s",
+	strbuf_addf(&buf, "%s%s\t%s",
 		    color(WT_STATUS_HEADER, s),
+		    s->display_comment_prefix ? "#" : "",
 		    color(WT_STATUS_UNTRACKED, s));
 	memset(&copts, 0, sizeof(copts));
 	copts.padding = 1;
@@ -730,6 +763,8 @@
 	print_columns(&output, s->colopts, &copts);
 	string_list_clear(&output, 0);
 	strbuf_release(&buf);
+conclude:
+	status_printf_ln(s, GIT_COLOR_NORMAL, "");
 }
 
 static void wt_status_print_verbose(struct wt_status *s)
@@ -764,6 +799,8 @@
 	struct strbuf sb = STRBUF_INIT;
 	const char *cp, *ep;
 	struct branch *branch;
+	char comment_line_string[3];
+	int i;
 
 	assert(s->branch && !s->is_initial);
 	if (prefixcmp(s->branch, "refs/heads/"))
@@ -772,12 +809,22 @@
 	if (!format_tracking_info(branch, &sb))
 		return;
 
+	i = 0;
+	if (s->display_comment_prefix) {
+		comment_line_string[i++] = comment_line_char;
+		comment_line_string[i++] = ' ';
+	}
+	comment_line_string[i] = '\0';
+
 	for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
 		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
-				 "%c %.*s", comment_line_char,
+				 "%s%.*s", comment_line_string,
 				 (int)(ep - cp), cp);
-	color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
-			 comment_line_char);
+	if (s->display_comment_prefix)
+		color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
+				 comment_line_char);
+	else
+		fprintf_ln(s->fp, "");
 }
 
 static int has_unmerged(struct wt_status *s)
@@ -799,13 +846,13 @@
 {
 	if (has_unmerged(s)) {
 		status_printf_ln(s, color, _("You have unmerged paths."));
-		if (advice_status_hints)
+		if (s->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)
+		if (s->hints)
 			status_printf_ln(s, color,
 				_("  (use \"git commit\" to conclude merge)"));
 	}
@@ -821,7 +868,7 @@
 	if (state->am_empty_patch)
 		status_printf_ln(s, color,
 			_("The current patch is empty."));
-	if (advice_status_hints) {
+	if (s->hints) {
 		if (!state->am_empty_patch)
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git am --continue\")"));
@@ -894,7 +941,7 @@
 		else
 			status_printf_ln(s, color,
 					 _("You are currently rebasing."));
-		if (advice_status_hints) {
+		if (s->hints) {
 			status_printf_ln(s, color,
 				_("  (fix conflicts and then run \"git rebase --continue\")"));
 			status_printf_ln(s, color,
@@ -911,7 +958,7 @@
 		else
 			status_printf_ln(s, color,
 					 _("You are currently rebasing."));
-		if (advice_status_hints)
+		if (s->hints)
 			status_printf_ln(s, color,
 				_("  (all conflicts fixed: run \"git rebase --continue\")"));
 	} else if (split_commit_in_progress(s)) {
@@ -923,7 +970,7 @@
 		else
 			status_printf_ln(s, color,
 					 _("You are currently splitting a commit during a rebase."));
-		if (advice_status_hints)
+		if (s->hints)
 			status_printf_ln(s, color,
 				_("  (Once your working directory is clean, run \"git rebase --continue\")"));
 	} else {
@@ -935,7 +982,7 @@
 		else
 			status_printf_ln(s, color,
 					 _("You are currently editing a commit during a rebase."));
-		if (advice_status_hints && !s->amend) {
+		if (s->hints && !s->amend) {
 			status_printf_ln(s, color,
 				_("  (use \"git commit --amend\" to amend the current commit)"));
 			status_printf_ln(s, color,
@@ -950,7 +997,7 @@
 					const char *color)
 {
 	status_printf_ln(s, color, _("You are currently cherry-picking."));
-	if (advice_status_hints) {
+	if (s->hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
 				_("  (fix conflicts and run \"git cherry-pick --continue\")"));
@@ -969,7 +1016,7 @@
 {
 	status_printf_ln(s, color, _("You are currently reverting commit %s."),
 			 find_unique_abbrev(state->revert_head_sha1, DEFAULT_ABBREV));
-	if (advice_status_hints) {
+	if (s->hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
 				_("  (fix conflicts and run \"git revert --continue\")"));
@@ -993,7 +1040,7 @@
 	else
 		status_printf_ln(s, color,
 				 _("You are currently bisecting."));
-	if (advice_status_hints)
+	if (s->hints)
 		status_printf_ln(s, color,
 			_("  (use \"git bisect reset\" to get back to the original branch)"));
 	wt_status_print_trailer(s);
@@ -1231,7 +1278,7 @@
 		}
 	} else if (s->commitable)
 		status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
-			advice_status_hints
+			s->hints
 			? _(" (use -u option to show untracked files)") : "");
 
 	if (s->verbose)
@@ -1242,25 +1289,25 @@
 		else if (s->nowarn)
 			; /* nothing */
 		else if (s->workdir_dirty) {
-			if (advice_status_hints)
+			if (s->hints)
 				printf(_("no changes added to commit "
 					 "(use \"git add\" and/or \"git commit -a\")\n"));
 			else
 				printf(_("no changes added to commit\n"));
 		} else if (s->untracked.nr) {
-			if (advice_status_hints)
+			if (s->hints)
 				printf(_("nothing added to commit but untracked files "
 					 "present (use \"git add\" to track)\n"));
 			else
 				printf(_("nothing added to commit but untracked files present\n"));
 		} else if (s->is_initial) {
-			if (advice_status_hints)
+			if (s->hints)
 				printf(_("nothing to commit (create/copy files "
 					 "and use \"git add\" to track)\n"));
 			else
 				printf(_("nothing to commit\n"));
 		} else if (!s->show_untracked_files) {
-			if (advice_status_hints)
+			if (s->hints)
 				printf(_("nothing to commit (use -u to show untracked files)\n"));
 			else
 				printf(_("nothing to commit\n"));
@@ -1363,6 +1410,7 @@
 	const char *base;
 	const char *branch_name;
 	int num_ours, num_theirs;
+	int upstream_is_gone = 0;
 
 	color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## ");
 
@@ -1380,20 +1428,37 @@
 	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);
+
+	color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+
+	switch (stat_tracking_info(branch, &num_ours, &num_theirs)) {
+	case 0:
+		/* no base */
 		fputc(s->null_termination ? '\0' : '\n', s->fp);
 		return;
+	case -1:
+		/* with "gone" base */
+		upstream_is_gone = 1;
+		break;
+	default:
+		/* with base */
+		break;
 	}
 
 	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);
 
+	if (!upstream_is_gone && !num_ours && !num_theirs) {
+		fputc(s->null_termination ? '\0' : '\n', s->fp);
+		return;
+	}
+
 	color_fprintf(s->fp, header_color, " [");
-	if (!num_ours) {
+	if (upstream_is_gone) {
+		color_fprintf(s->fp, header_color, _("gone"));
+	} else if (!num_ours) {
 		color_fprintf(s->fp, header_color, _("behind "));
 		color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
 	} else if (!num_theirs) {
diff --git a/wt-status.h b/wt-status.h
index 9966c13..9341c56 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -50,6 +50,7 @@
 	enum commit_whence whence;
 	int nowarn;
 	int use_color;
+	int display_comment_prefix;
 	int relative_paths;
 	int submodule_summary;
 	int show_ignored_files;
@@ -59,6 +60,7 @@
 	unsigned colopts;
 	int null_termination;
 	int show_branch;
+	int hints;
 
 	/* These are computed during processing of the individual sections */
 	int commitable;