Merge branch 'ss/clone-guess-dir-name-simplify'

Code simplification.

* ss/clone-guess-dir-name-simplify:
  clone: simplify string handling in guess_dir_name()
diff --git a/.gitignore b/.gitignore
index a052419..422c538 100644
--- a/.gitignore
+++ b/.gitignore
@@ -184,6 +184,7 @@
 /test-delta
 /test-dump-cache-tree
 /test-dump-split-index
+/test-dump-untracked-cache
 /test-scrap-cache-tree
 /test-genrandom
 /test-hashmap
diff --git a/Documentation/RelNotes/2.5.0.txt b/Documentation/RelNotes/2.5.0.txt
new file mode 100644
index 0000000..3100b26
--- /dev/null
+++ b/Documentation/RelNotes/2.5.0.txt
@@ -0,0 +1,523 @@
+Git 2.5 Release Notes
+=====================
+
+Updates since v2.4
+------------------
+
+UI, Workflows & Features
+
+ * The bash completion script (in contrib/) learned a few options that
+   "git revert" takes.
+
+ * Whitespace breakages in deleted and context lines can also be
+   painted in the output of "git diff" and friends with the new
+   --ws-error-highlight option.
+
+ * List of commands shown by "git help" are grouped along the workflow
+   elements to help early learners.
+
+ * "git p4" now detects the filetype (e.g. binary) correctly even when
+   the files are opened exclusively.
+
+ * git p4 attempts to better handle branches in Perforce.
+
+ * "git p4" learned "--changes-block-size <n>" to read the changes in
+   chunks from Perforce, instead of making one call to "p4 changes"
+   that may trigger "too many rows scanned" error from Perforce.
+
+ * More workaround for Perforce's row number limit in "git p4".
+
+ * Unlike "$EDITOR" and "$GIT_EDITOR" that can hold the path to the
+   command and initial options (e.g. "/path/to/emacs -nw"), 'git p4'
+   did not let the shell interpolate the contents of the environment
+   variable that name the editor "$P4EDITOR" (and "$EDITOR", too).
+   This release makes it in line with the rest of Git, as well as with
+   Perforce.
+
+ * A new short-hand <branch>@{push} denotes the remote-tracking branch
+   that tracks the branch at the remote the <branch> would be pushed
+   to.
+
+ * "git show-branch --topics HEAD" (with no other arguments) did not
+   do anything interesting.  Instead, contrast the given revision
+   against all the local branches by default.
+
+ * A replacement for contrib/workdir/git-new-workdir that does not
+   rely on symbolic links and make sharing of objects and refs safer
+   by making the borrowee and borrowers aware of each other.
+
+   Consider this as still an experimental feature; the UI will likely
+   to change.
+
+ * Tweak the sample "store" backend of the credential helper to honor
+   XDG configuration file locations when specified.
+
+ * A heuristic we use to catch mistyped paths on the command line
+   "git <cmd> <revs> <pathspec>" is to make sure that all the non-rev
+   parameters in the later part of the command line are names of the
+   files in the working tree, but that means "git grep $str -- \*.c"
+   must always be disambiguated with "--", because nobody sane will
+   create a file whose name literally is asterisk-dot-see.  Loosen the
+   heuristic to declare that with a wildcard string the user likely
+   meant to give us a pathspec.
+
+ * "git merge FETCH_HEAD" learned that the previous "git fetch" could
+   be to create an Octopus merge, i.e. recording multiple branches
+   that are not marked as "not-for-merge"; this allows us to lose an
+   old style invocation "git merge <msg> HEAD $commits..." in the
+   implementation of "git pull" script; the old style syntax can now
+   be deprecated (but not removed yet).
+
+ * Filter scripts were run with SIGPIPE disabled on the Git side,
+   expecting that they may not read what Git feeds them to filter.
+   We however treated a filter that does not read its input fully
+   before exiting as an error.  We no longer do and ignore EPIPE
+   when writing to feed the filter scripts.
+
+   This changes semantics, but arguably in a good way.  If a filter
+   can produce its output without fully consuming its input using
+   whatever magic, we now let it do so, instead of diagnosing it
+   as a programming error.
+
+ * Instead of dying immediately upon failing to obtain a lock, the
+   locking (of refs etc) retries after a short while with backoff.
+
+ * Introduce http.<url>.SSLCipherList configuration variable to tweak
+   the list of cipher suite to be used with libcURL when talking with
+   https:// sites.
+
+ * "git subtree" script (in contrib/) used "echo -n" to produce
+   progress messages in a non-portable way.
+
+ * "git subtree" script (in contrib/) does not have --squash option
+   when pushing, but the documentation and help text pretended as if
+   it did.
+
+ * The Git subcommand completion (in contrib/) no longer lists credential
+   helpers among candidates; they are not something the end user would
+   invoke interactively.
+
+ * The index file can be taught with "update-index --untracked-cache"
+   to optionally remember already seen untracked files, in order to
+   speed up "git status" in a working tree with tons of cruft.
+
+ * "git mergetool" learned to drive WinMerge as a backend.
+
+ * "git upload-pack" that serves "git fetch" can be told to serve
+   commits that are not at the tip of any ref, as long as they are
+   reachable from a ref, with uploadpack.allowReachableSHA1InWant
+   configuration variable.
+
+ * "git cat-file --batch(-check)" learned the "--follow-symlinks"
+   option that follows an in-tree symbolic link when asked about an
+   object via extended SHA-1 syntax, e.g. HEAD:RelNotes that points at
+   Documentation/RelNotes/2.5.0.txt.  With the new option, the command
+   behaves as if HEAD:Documentation/RelNotes/2.5.0.txt was given as
+   input instead.
+
+   Consider this as still an experimental and incomplete feature:
+
+    - We may want to do the same for in-index objects, e.g.
+      asking for :RelNotes with this option should give
+      :Documentation/RelNotes/2.5.0.txt, too
+
+    - "git cat-file --follow-symlinks blob HEAD:RelNotes"
+      may also be something we want to allow in the future.
+
+ * "git send-email" learned the alias file format used by the sendmail
+   program (in a simplified form; we obviously do not feed pipes).
+
+ * "git am" learned am.threeWay configuration variable.
+
+ * Traditionally, external low-level 3-way merge drivers are expected
+   to produce their results based solely on the contents of the three
+   variants given in temporary files named by %O, %A and %B on their
+   command line.  Additionally allow them to look at the final path
+   (given by %P).
+
+ * "git blame" learned blame.showEmail configuration variable.
+
+ * "git apply" cannot diagnose a patch corruption when the breakage is
+   to mark the length of the hunk shorter than it really is on the
+   hunk header line "@@ -l,k +m,n @@"; one special case it could is
+   when the hunk becomes no-op (e.g. k == n == 2 for two-line context
+   patch output), and it learned to do so in this special case.
+
+ * Add the "--allow-unknown-type" option to "cat-file" to allow
+   inspecting loose objects of an experimental or a broken type.
+
+ * Many long-running operations show progress eye-candy, even when
+   they are later backgrounded.  Hide the eye-candy when the process
+   is sent to the background instead.
+   (merge a4fb76c lm/squelch-bg-progress later to maint).
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "unsigned char [20]" used throughout the code to represent object
+   names are being converted into a semi-opaque "struct object_id".
+   This effort is expected to interfere with other topics in flight,
+   but hopefully will give us one extra level of abstraction in the
+   end, when completed.
+
+ * for_each_ref() callback functions were taught to name the objects
+   not with "unsigned char sha1[20]" but with "struct object_id".
+
+ * Catch a programmer mistake to feed a pointer not an array to
+   ARRAY_SIZE() macro, by using a couple of GCC extensions.
+
+ * Some error messages in "git config" were emitted without calling
+   the usual error() facility.
+
+ * When "add--interactive" splits a hunk into two overlapping hunks
+   and then let the user choose only one, it sometimes feeds an
+   incorrect patch text to "git apply".  Add tests to demonstrate
+   this.
+
+   I have a slight suspicion that this may be $gmane/87202 coming back
+   and biting us (I seem to have said "let's run with this and see
+   what happens" back then).
+
+ * More line-ending tests.
+
+ * An earlier rewrite to use strbuf_getwholeline() instead of fgets(3)
+   to read packed-refs file revealed that the former is unacceptably
+   inefficient.  It has been optimized by using getdelim(3) when
+   available.
+
+ * The refs API uses ref_lock struct which had its own "int fd", even
+   though the same file descriptor was in the lock struct it contains.
+   Clean-up the code to lose this redundant field.
+
+ * There was a dead code that used to handle "git pull --tags" and
+   show special-cased error message, which was made irrelevant when
+   the semantics of the option changed back in Git 1.9 days.
+   (merge 19d122b pt/pull-tags-error-diag later to maint).
+
+ * Help us to find broken test script that splits the body part of the
+   test by mistaken use of wrong kind of quotes.
+   (merge d93d5d5 jc/test-prereq-validate later to maint).
+
+ * Developer support to automatically detect broken &&-chain in the
+   test scripts is now turned on by default.
+   (merge 92b269f jk/test-chain-lint later to maint).
+
+ * Error reporting mechanism used in "refs" API has been made more
+   consistent.
+
+ * "git pull" has more test coverage now.
+
+ * "git pull" has become more aware of the options meant for
+   underlying "git fetch" and then learned to use parse-options
+   parser.
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.4
+----------------
+
+Unless otherwise noted, all the fixes since v2.4 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * Git 2.4 broke setting verbosity and progress levels on "git clone"
+   with native transports.
+   (merge 822f0c4 mh/clone-verbosity-fix later to maint).
+
+ * "git add -e" did not allow the user to abort the operation by
+   killing the editor.
+   (merge cb64800 jk/add-e-kill-editor later to maint).
+
+ * Memory usage of "git index-pack" has been trimmed by tens of
+   per-cent.
+   (merge f0e7f11 nd/slim-index-pack-memory-usage later to maint).
+
+ * "git rev-list --objects $old --not --all" to see if everything that
+   is reachable from $old is already connected to the existing refs
+   was very inefficient.
+   (merge b6e8a3b jk/still-interesting later to maint).
+
+ * "hash-object --literally" introduced in v2.2 was not prepared to
+   take a really long object type name.
+   (merge 1427a7f jc/hash-object later to maint).
+
+ * "git rebase --quiet" was not quite quiet when there is nothing to
+   do.
+   (merge 22946a9 jk/rebase-quiet-noop later to maint).
+
+ * The completion for "log --decorate=" parameter value was incorrect.
+   (merge af16bda sg/complete-decorate-full-not-long later to maint).
+
+ * "filter-branch" corrupted commit log message that ends with an
+   incomplete line on platforms with some "sed" implementations that
+   munge such a line.  Work it around by avoiding to use "sed".
+   (merge df06201 jk/filter-branch-use-of-sed-on-incomplete-line later to maint).
+
+ * "git daemon" fails to build from the source under NO_IPV6
+   configuration (regression in 2.4).
+   (merge d358f77 jc/daemon-no-ipv6-for-2.4.1 later to maint).
+
+ * Some time ago, "git blame" (incorrectly) lost the convert_to_git()
+   call when synthesizing a fake "tip" commit that represents the
+   state in the working tree, which broke folks who record the history
+   with LF line ending to make their project portable across platforms
+   while terminating lines in their working tree files with CRLF for
+   their platform.
+   (merge 4bf256d tb/blame-resurrect-convert-to-git later to maint).
+
+ * We avoid setting core.worktree when the repository location is the
+   ".git" directory directly at the top level of the working tree, but
+   the code misdetected the case in which the working tree is at the
+   root level of the filesystem (which arguably is a silly thing to
+   do, but still valid).
+   (merge 84ccad8 jk/init-core-worktree-at-root later to maint).
+
+ * "git commit --date=now" or anything that relies on approxidate lost
+   the daylight-saving-time offset.
+   (merge f6e6362 jc/epochtime-wo-tz later to maint).
+
+ * Access to objects in repositories that borrow from another one on a
+   slow NFS server unnecessarily got more expensive due to recent code
+   becoming more cautious in a naive way not to lose objects to pruning.
+   (merge ee1c6c3 jk/prune-mtime later to maint).
+
+ * The codepaths that read .gitignore and .gitattributes files have been
+   taught that these files encoded in UTF-8 may have UTF-8 BOM marker at
+   the beginning; this makes it in line with what we do for configuration
+   files already.
+   (merge 27547e5 cn/bom-in-gitignore later to maint).
+
+ * a few helper scripts in the test suite did not report errors
+   correctly.
+   (merge de248e9 ep/fix-test-lib-functions-report later to maint).
+
+ * The default $HOME/.gitconfig file created upon "git config --global"
+   that edits it had incorrectly spelled user.name and user.email
+   entries in it.
+   (merge 7e11052 oh/fix-config-default-user-name-section later to maint).
+
+ * "git cat-file bl $blob" failed to barf even though there is no
+   object type that is "bl".
+   (merge b7994af jk/type-from-string-gently later to maint).
+
+ * The usual "git diff" when seeing a file turning into a directory
+   showed a patchset to remove the file and create all files in the
+   directory, but "git diff --no-index" simply refused to work.  Also,
+   when asked to compare a file and a directory, imitate POSIX "diff"
+   and compare the file with the file with the same name in the
+   directory, instead of refusing to run.
+   (merge 0615173 jc/diff-no-index-d-f later to maint).
+
+ * "git rebase -i" moved the "current" command from "todo" to "done" a
+   bit too prematurely, losing a step when a "pick" did not even start.
+   (merge 8cbc57c ph/rebase-i-redo later to maint).
+
+ * The connection initiation code for "ssh" transport tried to absorb
+   differences between the stock "ssh" and Putty-supplied "plink" and
+   its derivatives, but the logic to tell that we are using "plink"
+   variants were too loose and falsely triggered when "plink" appeared
+   anywhere in the path (e.g. "/home/me/bin/uplink/ssh").
+   (merge baaf233 bc/connect-plink later to maint).
+
+ * We have prepended $GIT_EXEC_PATH and the path "git" is installed in
+   (typically "/usr/bin") to $PATH when invoking subprograms and hooks
+   for almost eternity, but the original use case the latter tried to
+   support was semi-bogus (i.e. install git to /opt/foo/git and run it
+   without having /opt/foo on $PATH), and more importantly it has
+   become less and less relevant as Git grew more mainstream (i.e. the
+   users would _want_ to have it on their $PATH).  Stop prepending the
+   path in which "git" is installed to users' $PATH, as that would
+   interfere the command search order people depend on (e.g. they may
+   not like versions of programs that are unrelated to Git in /usr/bin
+   and want to override them by having different ones in /usr/local/bin
+   and have the latter directory earlier in their $PATH).
+   (merge a0b4507 jk/git-no-more-argv0-path-munging later to maint).
+
+ * core.excludesfile (defaulting to $XDG_HOME/git/ignore) is supposed
+   to be overridden by repository-specific .git/info/exclude file, but
+   the order was swapped from the beginning. This belatedly fixes it.
+   (merge 099d2d8 jc/gitignore-precedence later to maint).
+
+ * There was a commented-out (instead of being marked to expect
+   failure) test that documented a breakage that was fixed since the
+   test was written; turn it into a proper test.
+   (merge 66d2e04 sb/t1020-cleanup later to maint).
+
+ * The "log --decorate" enhancement in Git 2.4 that shows the commit
+   at the tip of the current branch e.g. "HEAD -> master", did not
+   work with --decorate=full.
+   (merge 429ad20 mg/log-decorate-HEAD later to maint).
+
+ * The ref API did not handle cases where 'refs/heads/xyzzy/frotz' is
+   removed at the same time as 'refs/heads/xyzzy' is added (or vice
+   versa) very well.
+   (merge c628edf mh/ref-directory-file later to maint).
+
+ * Multi-ref transaction support we merged a few releases ago
+   unnecessarily kept many file descriptors open, risking to fail with
+   resource exhaustion.  This is for 2.4.x track.
+   (merge 185ce3a mh/write-refs-sooner-2.4 later to maint).
+
+ * "git bundle verify" did not diagnose extra parameters on the
+   command line.
+   (merge 7886cfa ps/bundle-verify-arg later to maint).
+
+ * Various documentation mark-up fixes to make the output more
+   consistent in general and also make AsciiDoctor (an alternative
+   formatter) happier.
+   (merge d0258b9 jk/asciidoc-markup-fix later to maint).
+   (merge ad3967a jk/stripspace-asciidoctor-fix later to maint).
+   (merge 975e382 ja/tutorial-asciidoctor-fix later to maint).
+
+ * The code to read pack-bitmap wanted to allocate a few hundred
+   pointers to a structure, but by mistake allocated and leaked memory
+   enough to hold that many actual structures.  Correct the allocation
+   size and also have it on stack, as it is small enough.
+   (merge 599dc76 rs/plug-leak-in-pack-bitmaps later to maint).
+
+ * The pull.ff configuration was supposed to override the merge.ff
+   configuration, but it didn't.
+   (merge db9bb28 pt/pull-ff-vs-merge-ff later to maint).
+
+ * "git pull --log" and "git pull --no-log" worked as expected, but
+   "git pull --log=20" did not.
+   (merge 5061a44 pt/pull-log-n later to maint).
+
+ * "git rerere forget" in a repository without rerere enabled gave a
+   cryptic error message; it should be a silent no-op instead.
+   (merge 0544574 jk/rerere-forget-check-enabled later to maint).
+
+ * "git rebase -i" fired post-rewrite hook when it shouldn't (namely,
+   when it was told to stop sequencing with 'exec' insn).
+   (merge 141ff8f mm/rebase-i-post-rewrite-exec later to maint).
+
+ * Clarify that "log --raw" and "log --format=raw" are unrelated
+   concepts.
+   (merge 92de921 mm/log-format-raw-doc later to maint).
+
+ * Make "git stash something --help" error out, so that users can
+   safely say "git stash drop --help".
+   (merge 5ba2831 jk/stash-options later to maint).
+
+ * The clean/smudge interface did not work well when filtering an
+   empty contents (failed and then passed the empty input through).
+   It can be argued that a filter that produces anything but empty for
+   an empty input is nonsense, but if the user wants to do strange
+   things, then why not?
+   (merge f6a1e1e jh/filter-empty-contents later to maint).
+
+ * Communication between the HTTP server and http_backend process can
+   lead to a dead-lock when relaying a large ref negotiation request.
+   Diagnose the situation better, and mitigate it by reading such a
+   request first into core (to a reasonable limit).
+   (merge 636614f jk/http-backend-deadlock later to maint).
+
+ * "git clean pathspec..." tried to lstat(2) and complain even for
+   paths outside the given pathspec.
+   (merge 838d6a9 dt/clean-pathspec-filter-then-lstat later to maint).
+
+ * Recent "git prune" traverses young unreachable objects to safekeep
+   old objects in the reachability chain from them, which sometimes
+   caused error messages that are unnecessarily alarming.
+   (merge ce4e7b2 jk/squelch-missing-link-warning-for-unreachable later to maint).
+
+ * The configuration reader/writer uses mmap(2) interface to access
+   the files; when we find a directory, it barfed with "Out of memory?".
+   (merge 9ca0aaf jk/diagnose-config-mmap-failure later to maint).
+
+ * "color.diff.plain" was a misnomer; give it 'color.diff.context' as
+   a more logical synonym.
+   (merge 8dbf3eb jk/color-diff-plain-is-context later to maint).
+
+ * The setup code used to die when core.bare and core.worktree are set
+   inconsistently, even for commands that do not need working tree.
+   (merge fada767 jk/die-on-bogus-worktree-late later to maint).
+
+ * Recent Mac OS X updates breaks the logic to detect that the machine
+   is on the AC power in the sample pre-auto-gc script.
+   (merge c54c7b3 pa/auto-gc-mac-osx later to maint).
+
+ * "git commit --cleanup=scissors" was not careful enough to protect
+   against getting fooled by a line that looked like scissors.
+   (merge fbfa097 sg/commit-cleanup-scissors later to maint).
+
+ * "Have we lost a race with competing repack?" check was too
+   expensive, especially while receiving a huge object transfer
+   that runs index-pack (e.g. "clone" or "fetch").
+   (merge 0eeb077 jk/index-pack-reduce-recheck later to maint).
+
+ * The tcsh completion writes a bash scriptlet but that would have
+   failed for users with noclobber set.
+   (merge 0b1f688 af/tcsh-completion-noclobber later to maint).
+
+ * "git for-each-ref" reported "missing object" for 0{40} when it
+   encounters a broken ref.  The lack of object whose name is 0{40} is
+   not the problem; the ref being broken is.
+   (merge 501cf47 mh/reporting-broken-refs-from-for-each-ref later to maint).
+
+ * Various fixes around "git am" that applies a patch to a history
+   that is not there yet.
+   (merge 6ea3b67 pt/am-abort-fix later to maint).
+
+ * "git fsck" used to ignore missing or invalid objects recorded in reflog.
+   (merge 19bf6c9 mh/fsck-reflog-entries later to maint).
+
+ * "git format-patch --ignore-if-upstream A..B" did not like to be fed
+   tags as boundary commits.
+   (merge 9b7a61d jc/do-not-feed-tags-to-clear-commit-marks later to maint).
+
+ * "git fetch --depth=<depth>" and "git clone --depth=<depth>" issued
+   a shallow transfer request even to an upload-pack that does not
+   support the capability.
+   (merge eb86a50 me/fetch-into-shallow-safety later to maint).
+
+ * "git rebase" did not exit with failure when format-patch it invoked
+   failed for whatever reason.
+   (merge 60d708b cb/rebase-am-exit-code later to maint).
+
+ * Fix a small bug in our use of umask() return value.
+   (merge 3096b2e jk/fix-refresh-utime later to maint).
+
+ * An ancient test framework enhancement to allow color was not
+   entirely correct; this makes it work even when tput needs to read
+   from the ~/.terminfo under the user's real HOME directory.
+   (merge d5c1b7c rh/test-color-avoid-terminfo-in-original-home later to maint).
+
+ * A minor bugfix when pack bitmap is used with "rev-list --count".
+   (merge c8a70d3 jk/rev-list-no-bitmap-while-pruning later to maint).
+
+ * Code cleanups and documentation updates.
+   (merge 0269f96 mm/usage-log-l-can-take-regex later to maint).
+   (merge 64f2589 nd/t1509-chroot-test later to maint).
+   (merge d201a1e sb/test-bitmap-free-at-end later to maint).
+   (merge 05bfc7d sb/line-log-plug-pairdiff-leak later to maint).
+   (merge 846e5df pt/xdg-config-path later to maint).
+   (merge 1154aa4 jc/plug-fmt-merge-msg-leak later to maint).
+   (merge 319b678 jk/sha1-file-reduce-useless-warnings later to maint).
+   (merge 9a35c14 fg/document-commit-message-stripping later to maint).
+   (merge bbf431c ps/doc-packfile-vs-pack-file later to maint).
+   (merge 309a9e3 jk/skip-http-tests-under-no-curl later to maint).
+   (merge ccd593c dl/branch-error-message later to maint).
+   (merge 22570b6 rs/janitorial later to maint).
+   (merge 5c2a581 mc/commit-doc-grammofix later to maint).
+   (merge ce41720 ah/usage-strings later to maint).
+   (merge e6a268c sb/glossary-submodule later to maint).
+   (merge ec48a76 sb/submodule-doc-intro later to maint).
+   (merge 14f8b9b jk/clone-dissociate later to maint).
+   (merge 055c7e9 sb/pack-protocol-mention-smart-http later to maint).
+   (merge 7c37a5d jk/make-fix-dependencies later to maint).
+   (merge fc0aa39 sg/merge-summary-config later to maint).
+   (merge 329af6c pt/t0302-needs-sanity later to maint).
+   (merge d614f07 fk/doc-format-patch-vn later to maint).
+   (merge 72dbb36 sg/completion-commit-cleanup later to maint).
+   (merge e654eb2 es/utf8-stupid-compiler-workaround later to maint).
+   (merge 34b935c es/osx-header-pollutes-mask-macro later to maint).
+   (merge ab7fade jc/prompt-document-ps1-state-separator later to maint).
+   (merge 25f600e mm/describe-doc later to maint).
+   (merge 83fe167 mm/branch-doc-updates later to maint).
+   (merge 75d2e5a ls/hint-rev-list-count later to maint).
+   (merge edc8f71 cb/subtree-tests-update later to maint).
+   (merge 5330e6e sb/p5310-and-chain later to maint).
+   (merge c4ac525 tb/checkout-doc later to maint).
+   (merge e479c5f jk/pretty-encoding-doc later to maint).
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 04f9977..5aa73cf 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -38,6 +38,10 @@
 	}
 }
 
+while (<>) {
+	last if /^### command list/;
+}
+
 my %cmds = ();
 for (sort <>) {
 	next if /^#/;
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2e919f0..3e37b93 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -453,6 +453,8 @@
 
 core.worktree::
 	Set the path to the root of the working tree.
+	If GIT_COMMON_DIR environment variable is set, core.worktree
+	is ignored and not used for determining the root of working tree.
 	This can be overridden by the GIT_WORK_TREE environment
 	variable and the '--work-tree' command-line option.
 	The value can be an absolute path or relative to the path to
@@ -622,6 +624,12 @@
 If set to "auto", `git-commit` would select a character that is not
 the beginning character of any line in existing commit messages.
 
+core.packedRefsTimeout::
+	The length of time, in milliseconds, to retry when trying to
+	lock the `packed-refs` file. Value 0 means not to retry at
+	all; -1 means to try indefinitely. Default is 1000 (i.e.,
+	retry for 1 second).
+
 sequence.editor::
 	Text editor used by `git rebase -i` for editing the rebase instruction file.
 	The value is meant to be interpreted by the shell when it is used.
@@ -761,6 +769,14 @@
 	by giving '--no-keep-cr' from the command line.
 	See linkgit:git-am[1], linkgit:git-mailsplit[1].
 
+am.threeWay::
+	By default, `git am` will fail if the patch does not apply cleanly. When
+	set to true, this setting tells `git am` to fall back on 3-way merge if
+	the patch records the identity of blobs it is supposed to apply to and
+	we have those blobs available locally (equivalent to giving the `--3way`
+	option from the command line). Defaults to `false`.
+	See linkgit:git-am[1].
+
 apply.ignoreWhitespace::
 	When set to 'change', tells 'git apply' to ignore changes in
 	whitespace, in the same way as the '--ignore-space-change'
@@ -1275,6 +1291,13 @@
 	"now" may be used to disable this  grace period and always prune
 	unreachable objects immediately.
 
+gc.pruneWorktreesExpire::
+	When 'git gc' is run, it will call
+	'prune --worktrees --expire 3.months.ago'.
+	Override the grace period with this config variable. The value
+	"now" may be used to disable the grace period and prune
+	$GIT_DIR/worktrees immediately.
+
 gc.reflogExpire::
 gc.<pattern>.reflogExpire::
 	'git reflog expire' removes reflog entries older than
@@ -1561,6 +1584,19 @@
 	If set, store cookies received during requests to the file specified by
 	http.cookieFile. Has no effect if http.cookieFile is unset.
 
+http.sslCipherList::
+  A list of SSL ciphers to use when negotiating an SSL connection.
+  The available ciphers depend on whether libcurl was built against
+  NSS or OpenSSL and the particular configuration of the crypto
+  library in use.  Internally this sets the 'CURLOPT_SSL_CIPHER_LIST'
+  option; see the libcurl documentation for more details on the format
+  of this list.
++
+Can be overridden by the 'GIT_SSL_CIPHER_LIST' environment variable.
+To force git to use libcurl's default cipher list and ignore any
+explicit http.sslCipherList option, set 'GIT_SSL_CIPHER_LIST' to the
+empty string.
+
 http.sslVerify::
 	Whether to verify the SSL certificate when fetching or pushing
 	over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
@@ -2531,14 +2567,20 @@
 	are under the hierarchies listed on the value of this
 	variable is excluded, and is hidden from `git ls-remote`,
 	`git fetch`, etc.  An attempt to fetch a hidden ref by `git
-	fetch` will fail.  See also `uploadpack.allowtipsha1inwant`.
+	fetch` will fail.  See also `uploadpack.allowTipSHA1InWant`.
 
-uploadpack.allowtipsha1inwant::
+uploadpack.allowTipSHA1InWant::
 	When `uploadpack.hideRefs` is in effect, allow `upload-pack`
 	to accept a fetch request that asks for an object at the tip
 	of a hidden ref (by default, such a request is rejected).
 	see also `uploadpack.hideRefs`.
 
+uploadpack.allowReachableSHA1InWant::
+	Allow `upload-pack` to accept a fetch request that asks for an
+	object that is reachable from any ref tip. However, note that
+	calculating object reachability is computationally expensive.
+	Defaults to `false`.
+
 uploadpack.keepAlive::
 	When `upload-pack` has started `pack-objects`, there may be a
 	quiet period while `pack-objects` prepares the pack. Normally
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3ad6404..d56ca90 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -291,6 +291,16 @@
 	initial indent of the line are considered whitespace errors.
 	Exits with non-zero status if problems are found. Not compatible
 	with --exit-code.
+
+--ws-error-highlight=<kind>::
+	Highlight whitespace errors on lines specified by <kind>
+	in the color specified by `color.diff.whitespace`.  <kind>
+	is a comma separated list of `old`, `new`, `context`.  When
+	this option is not given, only whitespace errors in `new`
+	lines are highlighted.  E.g. `--ws-error-highlight=new,old`
+	highlights whitespace errors on both deleted and added lines.
+	`all` can be used as a short-hand for `old,new,context`.
+
 endif::git-format-patch[]
 
 --full-index::
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0d8ba48..dbea6e7 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
-	 [--3way] [--interactive] [--committer-date-is-author-date]
+	 [--[no-]3way] [--interactive] [--committer-date-is-author-date]
 	 [--ignore-date] [--ignore-space-change | --ignore-whitespace]
 	 [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
@@ -90,10 +90,13 @@
 
 -3::
 --3way::
+--no-3way::
 	When the patch does not apply cleanly, fall back on
 	3-way merge if the patch records the identity of blobs
 	it is supposed to apply to and we have those blobs
-	available locally.
+	available locally. `--no-3way` can be used to override
+	am.threeWay configuration variable. For more information,
+	see am.threeWay in linkgit:git-config[1].
 
 --ignore-space-change::
 --ignore-whitespace::
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 9f23a86..e6e947c 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -76,6 +76,8 @@
 -e::
 --show-email::
 	Show the author email instead of author name (Default: off).
+	This can also be controlled via the `blame.showEmail` config
+	option.
 
 -w::
 	Ignore whitespace when comparing the parent's version and
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 359619b..a67138a 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -81,7 +81,7 @@
 	`--track` or `--set-upstream`.
 
 -D::
-	Delete a branch irrespective of its merged status.
+	Shortcut for `--delete --force`.
 
 -l::
 --create-reflog::
@@ -95,13 +95,17 @@
 --force::
 	Reset <branchname> to <startpoint> if <branchname> exists
 	already. Without `-f` 'git branch' refuses to change an existing branch.
+	In combination with `-d` (or `--delete`), allow deleting the
+	branch irrespective of its merged status. In combination with
+	`-m` (or `--move`), allow renaming the branch even if the new
+	branch name already exists.
 
 -m::
 --move::
 	Move/rename a branch and the corresponding reflog.
 
 -M::
-	Move/rename a branch even if the new branch name already exists.
+	Shortcut for `--move --force`.
 
 --color[=<when>]::
 	Color branches to highlight current, local, and
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index f6a16f4..319ab4c 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -9,8 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-'git cat-file' (-t | -s | -e | -p | <type> | --textconv ) <object>
-'git cat-file' (--batch | --batch-check) < <list-of-objects>
+'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv ) <object>
+'git cat-file' (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>
 
 DESCRIPTION
 -----------
@@ -69,6 +69,65 @@
 	not be combined with any other options or arguments.  See the
 	section `BATCH OUTPUT` below for details.
 
+--allow-unknown-type::
+	Allow -s or -t to query broken/corrupt objects of unknown type.
+
+--follow-symlinks::
+	With --batch or --batch-check, follow symlinks inside the
+	repository when requesting objects with extended SHA-1
+	expressions of the form tree-ish:path-in-tree.  Instead of
+	providing output about the link itself, provide output about
+	the linked-to object.  If a symlink points outside the
+	tree-ish (e.g. a link to /foo or a root-level link to ../foo),
+	the portion of the link which is outside the tree will be
+	printed.
++
+This option does not (currently) work correctly when an object in the
+index is specified (e.g. `:link` instead of `HEAD:link`) rather than
+one in the tree.
++
+This option cannot (currently) be used unless `--batch` or
+`--batch-check` is used.
++
+For example, consider a git repository containing:
++
+--
+	f: a file containing "hello\n"
+	link: a symlink to f
+	dir/link: a symlink to ../f
+	plink: a symlink to ../f
+	alink: a symlink to /etc/passwd
+--
++
+For a regular file `f`, `echo HEAD:f | git cat-file --batch` would print
++
+--
+	ce013625030ba8dba906f756967f9e9ca394464a blob 6
+--
++
+And `echo HEAD:link | git cat-file --batch --follow-symlinks` would
+print the same thing, as would `HEAD:dir/link`, as they both point at
+`HEAD:f`.
++
+Without `--follow-symlinks`, these would print data about the symlink
+itself.  In the case of `HEAD:link`, you would see
++
+--
+	4d1ae35ba2c8ec712fa2a379db44ad639ca277bd blob 1
+--
++
+Both `plink` and `alink` point outside the tree, so they would
+respectively print:
++
+--
+	symlink 4
+	../f
+
+	symlink 11
+	/etc/passwd
+--
+
+
 OUTPUT
 ------
 If '-t' is specified, one of the <type>.
@@ -148,6 +207,47 @@
 <object> SP missing LF
 ------------
 
+If --follow-symlinks is used, and a symlink in the repository points
+outside the repository, then `cat-file` will ignore any custom format
+and print:
+
+------------
+symlink SP <size> LF
+<symlink> LF
+------------
+
+The symlink will either be absolute (beginning with a /), or relative
+to the tree root.  For instance, if dir/link points to ../../foo, then
+<symlink> will be ../foo.  <size> is the size of the symlink in bytes.
+
+If --follow-symlinks is used, the following error messages will be
+displayed:
+
+------------
+<object> SP missing LF
+------------
+is printed when the initial symlink requested does not exist.
+
+------------
+dangling SP <size> LF
+<object> LF
+------------
+is printed when the initial symlink exists, but something that
+it (transitive-of) points to does not.
+
+------------
+loop SP <size> LF
+<object> LF
+------------
+is printed for symlink loops (or any symlinks that
+require more than 40 link resolutions to resolve).
+
+------------
+notdir SP <size> LF
+<object> LF
+------------
+is printed when, during symlink resolution, a file is used as a
+directory name.
 
 CAVEATS
 -------
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index d504108..2df6d0d 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-checkout - Checkout a branch or paths to the working tree
+git-checkout - Switch branches or restore working tree files
 
 SYNOPSIS
 --------
@@ -89,6 +89,10 @@
 	(i.e.  commit, tag or tree) to update the index for the given
 	paths before updating the working tree.
 +
+'git checkout' with <paths> or `--patch` is used to restore modified or
+deleted paths to their original contents from the index or replace paths
+with the contents from a named <tree-ish> (most often a commit-ish).
++
 The index may contain unmerged entries because of a previous failed merge.
 By default, if you try to check out such an entry from the index, the
 checkout operation will fail and nothing will be checked out.
@@ -225,6 +229,19 @@
 edits from your current working tree. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
+--to=<path>::
+	Check out a branch in a separate working directory at
+	`<path>`. A new working directory is linked to the current
+	repository, sharing everything except working directory
+	specific files such as HEAD, index... See "MULTIPLE WORKING
+	TREES" section for more information.
+
+--ignore-other-worktrees::
+	`git checkout` refuses when the wanted ref is already checked
+	out by another worktree. This option makes it check the ref
+	out anyway. In other words, the ref can be held by more than one
+	worktree.
+
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
@@ -388,6 +405,71 @@
 $ git log -g -2 HEAD
 ------------
 
+MULTIPLE WORKING TREES
+----------------------
+
+A git repository can support multiple working trees, allowing you to check
+out more than one branch at a time.  With `git checkout --to` a new working
+tree is associated with the repository.  This new working tree is called a
+"linked working tree" as opposed to the "main working tree" prepared by "git
+init" or "git clone".  A repository has one main working tree (if it's not a
+bare repository) and zero or more linked working trees.
+
+Each linked working tree has a private sub-directory in the repository's
+$GIT_DIR/worktrees directory.  The private sub-directory's name is usually
+the base name of the linked working tree's path, possibly appended with a
+number to make it unique.  For example, when `$GIT_DIR=/path/main/.git` the
+command `git checkout --to /path/other/test-next next` creates the linked
+working tree in `/path/other/test-next` and also creates a
+`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1`
+if `test-next` is already taken).
+
+Within a linked working tree, $GIT_DIR is set to point to this private
+directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and
+$GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR
+(e.g. `/path/main/.git`). These settings are made in a `.git` file located at
+the top directory of the linked working tree.
+
+Path resolution via `git rev-parse --git-path` uses either
+$GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the
+linked working tree `git rev-parse --git-path HEAD` returns
+`/path/main/.git/worktrees/test-next/HEAD` (not
+`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
+rev-parse --git-path refs/heads/master` uses
+$GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,
+since refs are shared across all working trees.
+
+See linkgit:gitrepository-layout[5] for more information. The rule of
+thumb is do not make any assumption about whether a path belongs to
+$GIT_DIR or $GIT_COMMON_DIR when you need to directly access something
+inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path.
+
+When you are done with a linked working tree you can simply delete it.
+The working tree's entry in the repository's $GIT_DIR/worktrees
+directory will eventually be removed automatically (see
+`gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run
+`git prune --worktrees` in the main or any linked working tree to
+clean up any stale entries in $GIT_DIR/worktrees.
+
+If you move a linked working directory to another file system, or
+within a file system that does not support hard links, you need to run
+at least one git command inside the linked working directory
+(e.g. `git status`) in order to update its entry in $GIT_DIR/worktrees
+so that it does not get automatically removed.
+
+To prevent a $GIT_DIR/worktrees entry from from being pruned (which
+can be useful in some situations, such as when the
+entry's working tree is stored on a portable device), add a file named
+'locked' to the entry's directory. The file contains the reason in
+plain text. For example, if a linked working tree's `.git` file points
+to `/path/main/.git/worktrees/test-next` then a file named
+`/path/main/.git/worktrees/test-next/locked` will prevent the
+`test-next` entry from being pruned.  See
+linkgit:gitrepository-layout[5] for details.
+
+Multiple checkout support for submodules is incomplete. It is NOT
+recommended to make multiple checkouts of a superproject.
+
 EXAMPLES
 --------
 
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index d20ca40..e045fc7 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-describe - Show the most recent tag that is reachable from a commit
+git-describe - Describe a commit using the most recent tag reachable from it
 
 
 SYNOPSIS
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 4240875..7f8d9a5 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -97,6 +97,12 @@
 	or "=" (in sync).  Has no effect if the ref does not have
 	tracking information associated with it.
 
+push::
+	The name of a local ref which represents the `@{push}` location
+	for the displayed ref. Respects `:short`, `:track`, and
+	`:trackshort` options as `upstream` does. Produces an empty
+	string if no `@{push}` ref is configured.
+
 HEAD::
 	'*' if HEAD matches current ref (the checked out branch), ' '
 	otherwise.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index bb3ea93..0dac4e9 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -170,7 +170,7 @@
 -v <n>::
 --reroll-count=<n>::
 	Mark the series as the <n>-th iteration of the topic. The
-	output filenames have `v<n>` pretended to them, and the
+	output filenames have `v<n>` prepended to them, and the
 	subject prefix ("PATCH" by default, but configurable via the
 	`--subject-prefix` option) has ` v<n>` appended to it.  E.g.
 	`--reroll-count=4` may produce `v4-0001-add-makefile.patch`
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 1f94908..273a100 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -104,6 +104,10 @@
 If no commit is given from the command line, merge the remote-tracking
 branches that the current branch is configured to use as its upstream.
 See also the configuration section of this manual page.
++
+When `FETCH_HEAD` (and no other commit) is specified, the branches
+recorded in the `.git/FETCH_HEAD` file by the previous invocation
+of `git fetch` for merging are merged to the current branch.
 
 
 PRE-MERGE CHECKS
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index a1664b9..82aa5d6 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -225,9 +225,20 @@
 	they can find the p4 branches in refs/heads.
 
 --max-changes <n>::
-	Limit the number of imported changes to 'n'.  Useful to
-	limit the amount of history when using the '@all' p4 revision
-	specifier.
+	Import at most 'n' changes, rather than the entire range of
+	changes included in the given revision specifier. A typical
+	usage would be use '@all' as the revision specifier, but then
+	to use '--max-changes 1000' to import only the last 1000
+	revisions rather than the entire revision history.
+
+--changes-block-size <n>::
+	The internal block size to use when converting a revision
+	specifier such as '@all' into a list of specific change
+	numbers. Instead of using a single call to 'p4 changes' to
+	find the full list of changes for the conversion, there are a
+	sequence of calls to 'p4 changes -m', each of which requests
+	one block of changes of the given size. The default block size
+	is 500, which should usually be suitable.
 
 --keep-path::
 	The mapping of file names from the p4 depot path to Git, by
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 7a493c8..1cf3bed 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -48,6 +48,9 @@
 --expire <time>::
 	Only expire loose objects older than <time>.
 
+--worktrees::
+	Prune dead working tree information in $GIT_DIR/worktrees.
+
 <head>...::
 	In addition to objects
 	reachable from any of our references, keep objects
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 712ab4b..93c72a2 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -74,9 +74,6 @@
 OPTIONS
 -------
 
-Options meant for 'git pull' itself and the underlying 'git merge'
-must be given before the options meant for 'git fetch'.
-
 -q::
 --quiet::
 	This is passed to both underlying git-fetch to squelch reporting of
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index b10ea60..7b49c85 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -56,6 +56,7 @@
 	     [ --reverse ]
 	     [ --walk-reflogs ]
 	     [ --no-walk ] [ --do-walk ]
+	     [ --count ]
 	     [ --use-bitmap-index ]
 	     <commit>... [ \-- <paths>... ]
 
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index bf81b97..c483100 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -216,6 +216,9 @@
 is not detected to lie in a Git repository or work tree
 print a message to stderr and exit with nonzero status.
 
+--git-common-dir::
+	Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
+
 --is-inside-git-dir::
 	When the current working directory is below the repository
 	directory print "true", otherwise "false".
@@ -233,6 +236,13 @@
 	repository.  If <path> is a gitfile then the resolved path
 	to the real repository is printed.
 
+--git-path <path>::
+	Resolve "$GIT_DIR/<path>" and takes other path relocation
+	variables such as $GIT_OBJECT_DIRECTORY,
+	$GIT_INDEX_FILE... into account. For example, if
+	$GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
+	--git-path objects/abc" returns /foo/bar/abc.
+
 --show-cdup::
 	When the command is invoked from a subdirectory, show the
 	path of the top-level directory relative to the current
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 8045546..7ae467b 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -383,7 +383,24 @@
 
 sendemail.aliasFileType::
 	Format of the file(s) specified in sendemail.aliasesFile. Must be
-	one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus'.
+	one of 'mutt', 'mailrc', 'pine', 'elm', or 'gnus', or 'sendmail'.
++
+What an alias file in each format looks like can be found in
+the documentation of the email program of the same name. The
+differences and limitations from the standard formats are
+described below:
++
+--
+sendmail;;
+*	Quoted aliases and quoted addresses are not supported: lines that
+	contain a `"` symbol are ignored.
+*	Redirection to a file (`/path/name`) or pipe (`|command`) is not
+	supported.
+*	File inclusion (`:include: /path/name`) is not supported.
+*	Warnings are printed on the standard error output for any
+	explicitly unsupported constructs, and any other lines that are not
+	recognized by the parser.
+--
 
 sendemail.multiEdit::
 	If true (default), a single editor instance will be spawned to edit
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 5221f95..335f312 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -66,7 +66,10 @@
 shown (i.e. the same as specifying `normal`), to help you avoid
 forgetting to add newly created files.  Because it takes extra work
 to find untracked files in the filesystem, this mode may take some
-time in a large working tree.  You can use `no` to have `git status`
+time in a large working tree.
+Consider enabling untracked cache and split index if supported (see
+`git update-index --untracked-cache` and `git update-index
+--split-index`), Otherwise you can use `no` to have `git status`
 return more quickly without showing untracked files.
 +
 The default can be changed using the status.showUntrackedFiles
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index aff0179..1a296bc 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -170,6 +170,20 @@
 	the shared index file. This mode is designed for very large
 	indexes that take a significant amount of time to read or write.
 
+--untracked-cache::
+--no-untracked-cache::
+	Enable or disable untracked cache extension. This could speed
+	up for commands that involve determining untracked files such
+	as `git status`. The underlying operating system and file
+	system must change `st_mtime` field of a directory if files
+	are added or deleted in that directory.
+
+--force-untracked-cache::
+	For safety, `--untracked-cache` performs tests on the working
+	directory to make sure untracked cache can be used. These
+	tests can take a few seconds. `--force-untracked-cache` can be
+	used to skip the tests.
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index a4a4cf3..613ce6b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -839,6 +839,15 @@
 	an explicit repository directory set via 'GIT_DIR' or on the
 	command line.
 
+'GIT_COMMON_DIR'::
+	If this variable is set to a path, non-worktree files that are
+	normally in $GIT_DIR will be taken from this path
+	instead. Worktree-specific files such as HEAD or index are
+	taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and
+	the section 'MULTIPLE CHECKOUT MODE' in linkgit:checkout[1]
+	details. This variable has lower precedence than other path
+	variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
+
 Git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 70899b3..81fe586 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -774,7 +774,7 @@
 ----------------------------------------------------------------
 [merge "filfre"]
 	name = feel-free merge driver
-	driver = filfre %O %A %B
+	driver = filfre %O %A %B %L %P
 	recursive = binary
 ----------------------------------------------------------------
 
@@ -800,6 +800,9 @@
 When left unspecified, the driver itself is used for both
 internal merge and the final merge.
 
+The merge driver can learn the pathname in which the merged result
+will be stored via placeholder `%P`.
+
 
 `conflict-marker-size`
 ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 79653f3..7173b38 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -46,6 +46,9 @@
 use with dumb transports but otherwise is OK as long as
 `objects/info/alternates` points at the object stores it
 borrows from.
++
+This directory is ignored if $GIT_COMMON_DIR is set and
+"$GIT_COMMON_DIR/objects" will be used instead.
 
 objects/[0-9a-f][0-9a-f]::
 	A newly created object is stored in its own file.
@@ -92,7 +95,8 @@
 	References are stored in subdirectories of this
 	directory.  The 'git prune' command knows to preserve
 	objects reachable from refs found in this directory and
-	its subdirectories.
+	its subdirectories. This directory is ignored if $GIT_COMMON_DIR
+	is set and "$GIT_COMMON_DIR/refs" will be used instead.
 
 refs/heads/`name`::
 	records tip-of-the-tree commit objects of branch `name`
@@ -114,7 +118,8 @@
 packed-refs::
 	records the same information as refs/heads/, refs/tags/,
 	and friends record in a more efficient way.  See
-	linkgit:git-pack-refs[1].
+	linkgit:git-pack-refs[1]. This file is ignored if $GIT_COMMON_DIR
+	is set and "$GIT_COMMON_DIR/packed-refs" will be used instead.
 
 HEAD::
 	A symref (see glossary) to the `refs/heads/` namespace
@@ -133,6 +138,11 @@
 is often called 'detached HEAD.'  See linkgit:git-checkout[1]
 for details.
 
+config::
+	Repository specific configuration file. This file is ignored
+	if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
+	used instead.
+
 branches::
 	A slightly deprecated way to store shorthands to be used
 	to specify a URL to 'git fetch', 'git pull' and 'git push'.
@@ -140,7 +150,10 @@
 	'name' can be given to these commands in place of
 	'repository' argument.  See the REMOTES section in
 	linkgit:git-fetch[1] for details.  This mechanism is legacy
-	and not likely to be found in modern repositories.
+	and not likely to be found in modern repositories. This
+	directory is ignored if $GIT_COMMON_DIR is set and
+	"$GIT_COMMON_DIR/branches" will be used instead.
+
 
 hooks::
 	Hooks are customization scripts used by various Git
@@ -149,7 +162,9 @@
 	default.  To enable, the `.sample` suffix has to be
 	removed from the filename by renaming.
 	Read linkgit:githooks[5] for more details about
-	each hook.
+	each hook. This directory is ignored if $GIT_COMMON_DIR is set
+	and "$GIT_COMMON_DIR/hooks" will be used instead.
+
 
 index::
 	The current index file for the repository.  It is
@@ -161,7 +176,8 @@
 
 info::
 	Additional information about the repository is recorded
-	in this directory.
+	in this directory. This directory is ignored if $GIT_COMMON_DIR
+	is set and "$GIT_COMMON_DIR/index" will be used instead.
 
 info/refs::
 	This file helps dumb transports discover what refs are
@@ -201,12 +217,15 @@
 	when interacting with remote repositories via 'git fetch',
 	'git pull' and 'git push' commands.  See the REMOTES section
 	in linkgit:git-fetch[1] for details.  This mechanism is legacy
-	and not likely to be found in modern repositories.
+	and not likely to be found in modern repositories. This
+	directory is ignored if $GIT_COMMON_DIR is set and
+	"$GIT_COMMON_DIR/remotes" will be used instead.
 
 logs::
-	Records of changes made to refs are stored in this
-	directory.  See linkgit:git-update-ref[1]
-	for more information.
+	Records of changes made to refs are stored in this directory.
+	See linkgit:git-update-ref[1] for more information. This
+	directory is ignored if $GIT_COMMON_DIR is set and
+	"$GIT_COMMON_DIR/logs" will be used instead.
 
 logs/refs/heads/`name`::
 	Records all changes made to the branch tip named `name`.
@@ -217,11 +236,46 @@
 shallow::
 	This is similar to `info/grafts` but is internally used
 	and maintained by shallow clone mechanism.  See `--depth`
-	option to linkgit:git-clone[1] and linkgit:git-fetch[1].
+	option to linkgit:git-clone[1] and linkgit:git-fetch[1]. This
+	file is ignored if $GIT_COMMON_DIR is set and
+	"$GIT_COMMON_DIR/shallow" will be used instead.
+
+commondir::
+	If this file exists, $GIT_COMMON_DIR (see linkgit:git[1]) will
+	be set to the path specified in this file if it is not
+	explicitly set. If the specified path is relative, it is
+	relative to $GIT_DIR. The repository with commondir is
+	incomplete without the repository pointed by "commondir".
 
 modules::
 	Contains the git-repositories of the submodules.
 
+worktrees::
+	Contains worktree specific information of linked
+	checkouts. Each subdirectory contains the worktree-related
+	part of a linked checkout. This directory is ignored if
+	$GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/worktrees" will be
+	used instead.
+
+worktrees/<id>/gitdir::
+	A text file containing the absolute path back to the .git file
+	that points to here. This is used to check if the linked
+	repository has been manually removed and there is no need to
+	keep this directory any more. mtime of this file should be
+	updated every time the linked repository is accessed.
+
+worktrees/<id>/locked::
+	If this file exists, the linked repository may be on a
+	portable device and not available. It does not mean that the
+	linked repository is gone and `worktrees/<id>` could be
+	removed. The file's content contains a reason string on why
+	the repository is locked.
+
+worktrees/<id>/link::
+	If this file exists, it is a hard link to the linked .git
+	file. It is used to detect if the linked repository is
+	manually removed.
+
 SEE ALSO
 --------
 linkgit:git-init[1],
diff --git a/Documentation/howto/new-command.txt b/Documentation/howto/new-command.txt
index d7de5a3..6d772bd 100644
--- a/Documentation/howto/new-command.txt
+++ b/Documentation/howto/new-command.txt
@@ -95,7 +95,9 @@
 that categorizes commands by type, so they can be listed in appropriate
 subsections in the documentation's summary command list.  Add an entry
 for yours.  To understand the categories, look at git-commands.txt
-in the main directory.
+in the main directory.  If the new command is part of the typical Git
+workflow and you believe it common enough to be mentioned in 'git help',
+map this command to a common group in the column [common].
 
 7. Give the maintainer one paragraph to include in the RelNotes file
 to describe the new feature; a good place to do so is in the cover
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 74aa01a..642af6e 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -37,7 +37,10 @@
 	in their encoding header; this option can be used to tell the
 	command to re-code the commit log message in the encoding
 	preferred by the user.  For non plumbing commands this
-	defaults to UTF-8.
+	defaults to UTF-8. Note that if an object claims to be encoded
+	in `X` and we are outputting in `X`, we will output the object
+	verbatim; this means that invalid sequences in the original
+	commit may be copied to the output.
 
 --notes[=<ref>]::
 	Show the notes (see linkgit:git-notes[1]) that annotate the
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 0796118..d85e303 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -98,6 +98,31 @@
   `branch.<name>.merge`).  A missing branchname defaults to the
   current one.
 
+'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
+  The suffix '@\{push}' reports the branch "where we would push to" if
+  `git push` were run while `branchname` was checked out (or the current
+  'HEAD' if no branchname is specified). Since our push destination is
+  in a remote repository, of course, we report the local tracking branch
+  that corresponds to that branch (i.e., something in 'refs/remotes/').
++
+Here's an example to make it more clear:
++
+------------------------------
+$ git config push.default current
+$ git config remote.pushdefault myfork
+$ git checkout -b mybranch origin/master
+
+$ git rev-parse --symbolic-full-name @{upstream}
+refs/remotes/origin/master
+
+$ git rev-parse --symbolic-full-name @{push}
+refs/remotes/myfork/mybranch
+------------------------------
++
+Note in the example that we set up a triangular workflow, where we pull
+from one location and push to another. In a non-triangular workflow,
+'@\{push}' is the same as '@\{upstream}', and there is no need for it.
+
 '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
diff --git a/Documentation/technical/api-ref-iteration.txt b/Documentation/technical/api-ref-iteration.txt
index 02adfd4..37379d8 100644
--- a/Documentation/technical/api-ref-iteration.txt
+++ b/Documentation/technical/api-ref-iteration.txt
@@ -6,7 +6,7 @@
 callback function for every ref. The callback function has this
 signature:
 
-	int handle_one_ref(const char *refname, const unsigned char *sha1,
+	int handle_one_ref(const char *refname, const struct object_id *oid,
 			   int flags, void *cb_data);
 
 There are different kinds of iterate functions which all take a
diff --git a/Documentation/technical/api-remote.txt b/Documentation/technical/api-remote.txt
index 5d245aa..2cfdd22 100644
--- a/Documentation/technical/api-remote.txt
+++ b/Documentation/technical/api-remote.txt
@@ -97,10 +97,6 @@
 
 	The name of the remote listed in the configuration.
 
-`remote`::
-
-	The struct remote for that remote.
-
 `merge_name`::
 
 	An array of the "merge" lines in the configuration.
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 229f845..1c561bd 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -319,7 +319,8 @@
 Clients MUST send at least one "want" command in the request body.
 Clients MUST NOT reference an id in a "want" command which did not
 appear in the response obtained through ref discovery unless the
-server advertises capability `allow-tip-sha1-in-want`.
+server advertises capability `allow-tip-sha1-in-want` or
+`allow-reachable-sha1-in-want`.
 
   compute_request   =  want_list
 		       have_list
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index 35112e4..b7093af 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -233,3 +233,65 @@
   The remaining index entries after replaced ones will be added to the
   final index. These added entries are also sorted by entry name then
   stage.
+
+== Untracked cache
+
+  Untracked cache saves the untracked file list and necessary data to
+  verify the cache. The signature for this extension is { 'U', 'N',
+  'T', 'R' }.
+
+  The extension starts with
+
+  - A sequence of NUL-terminated strings, preceded by the size of the
+    sequence in variable width encoding. Each string describes the
+    environment where the cache can be used.
+
+  - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
+    ctime field until "file size".
+
+  - Stat data of core.excludesfile
+
+  - 32-bit dir_flags (see struct dir_struct)
+
+  - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file
+    does not exist.
+
+  - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does
+    not exist.
+
+  - NUL-terminated string of per-dir exclude file name. This usually
+    is ".gitignore".
+
+  - The number of following directory blocks, variable width
+    encoding. If this number is zero, the extension ends here with a
+    following NUL.
+
+  - A number of directory blocks in depth-first-search order, each
+    consists of
+
+    - The number of untracked entries, variable width encoding.
+
+    - The number of sub-directory blocks, variable width encoding.
+
+    - The directory name terminated by NUL.
+
+    - A number of untrached file/dir names terminated by NUL.
+
+The remaining data of each directory block is grouped by type:
+
+  - An ewah bitmap, the n-th bit marks whether the n-th directory has
+    valid untracked cache entries.
+
+  - An ewah bitmap, the n-th bit records "check-only" bit of
+    read_directory_recursive() for the n-th directory.
+
+  - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data
+    is valid for the n-th directory and exists in the next data.
+
+  - An array of stat data. The n-th data corresponds with the n-th
+    "one" bit in the previous ewah bitmap.
+
+  - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit
+    in the previous ewah bitmap.
+
+  - One NUL.
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 4f8a7bf..eaab6b4 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -260,6 +260,13 @@
 send "want" lines with SHA-1s that exist at the server but are not
 advertised by upload-pack.
 
+allow-reachable-sha1-in-want
+----------------------------
+
+If the upload-pack server advertises this capability, fetch-pack may
+send "want" lines with SHA-1s that exist at the server but are not
+advertised by upload-pack.
+
 push-cert=<nonce>
 -----------------
 
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 242a044..4a8be4d 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -41,13 +41,17 @@
 compared, but this is not enabled by default because this member
 is not stable on network filesystems.  With `USE_NSEC`
 compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
-members are also compared, but this is not enabled by default
+members are also compared. On Linux, this is not enabled by default
 because in-core timestamps can have finer granularity than
 on-disk timestamps, resulting in meaningless changes when an
 inode is evicted from the inode cache.  See commit 8ce13b0
 of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
 ([PATCH] Sync in core time granularity with filesystems,
-2005-01-04).
+2005-01-04). This patch is included in kernel 2.6.11 and newer, but
+only fixes the issue for file systems with exactly 1 ns or 1 s
+resolution. Other file systems are still broken in current Linux
+kernels (e.g. CEPH, CIFS, NTFS, UDF), see
+https://lkml.org/lkml/2015/6/9/714
 
 Racy Git
 --------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 159d526..b34941c 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.4.5
+DEF_VER=v2.5.0-rc1
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 8700db8..895f002 100644
--- a/Makefile
+++ b/Makefile
@@ -217,10 +217,11 @@
 # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
-# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
-# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
-# randomly break unless your underlying filesystem supports those sub-second
-# times (my ext3 doesn't).
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
+# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
+# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
+# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
+# Documentation/technical/racy-git.txt for details.
 #
 # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
 # "st_ctim"
@@ -359,6 +360,8 @@
 # compiler is detected to support it.
 #
 # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
+#
+# Define HAVE_GETDELIM if your system has the getdelim() function.
 
 GIT-VERSION-FILE: FORCE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -572,6 +575,7 @@
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 TEST_PROGRAMS_NEED_X += test-dump-split-index
+TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
@@ -1437,6 +1441,10 @@
 	BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
 endif
 
+ifdef HAVE_GETDELIM
+	BASIC_CFLAGS += -DHAVE_GETDELIM
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK = NoThanks
 endif
@@ -1687,10 +1695,10 @@
 	ln -s $< $@ 2>/dev/null || \
 	cp $< $@
 
-common-cmds.h: ./generate-cmdlist.sh command-list.txt
+common-cmds.h: generate-cmdlist.perl command-list.txt
 
 common-cmds.h: $(wildcard Documentation/git-*.txt)
-	$(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
+	$(QUIET_GEN)$(PERL_PATH) generate-cmdlist.perl command-list.txt > $@+ && mv $@+ $@
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
 	$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@ -2449,7 +2457,7 @@
 		esac ; \
 		test -f "Documentation/$$v.txt" || \
 		echo "no doc: $$v"; \
-		sed -e '/^#/d' command-list.txt | \
+		sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
 		grep -q "^$$v[ 	]" || \
 		case "$$v" in \
 		git) ;; \
@@ -2457,7 +2465,8 @@
 		esac ; \
 	done; \
 	( \
-		sed -e '/^#/d' \
+		sed -e '1,/^### command list/d' \
+		    -e '/^#/d' \
 		    -e 's/[ 	].*//' \
 		    -e 's/^/listed /' command-list.txt; \
 		$(MAKE) -C Documentation print-man1 | \
diff --git a/RelNotes b/RelNotes
index 7b5f2ec..3295d66 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.4.5.txt
\ No newline at end of file
+Documentation/RelNotes/2.5.0.txt
\ No newline at end of file
diff --git a/archive-zip.c b/archive-zip.c
index ffb3535..ae3d67f 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -448,12 +448,12 @@
 	copy_le16(trailer.entries, zip_dir_entries);
 	copy_le32(trailer.size, zip_dir_offset);
 	copy_le32(trailer.offset, zip_offset);
-	copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+	copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);
 
 	write_or_die(1, zip_dir, zip_dir_offset);
 	write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
 	if (sha1)
-		write_or_die(1, sha1_to_hex(sha1), 40);
+		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
 static void dos_time(time_t *time, int *dos_date, int *dos_time)
diff --git a/archive.c b/archive.c
index 96057ed..d37c41d 100644
--- a/archive.c
+++ b/archive.c
@@ -101,7 +101,7 @@
 
 struct directory {
 	struct directory *up;
-	unsigned char sha1[20];
+	struct object_id oid;
 	int baselen, len;
 	unsigned mode;
 	int stage;
@@ -177,7 +177,7 @@
 	d->stage   = stage;
 	c->bottom  = d;
 	d->len = sprintf(d->path, "%.*s%s/", (int)base->len, base->buf, filename);
-	hashcpy(d->sha1, sha1);
+	hashcpy(d->oid.hash, sha1);
 }
 
 static int write_directory(struct archiver_context *c)
@@ -191,7 +191,7 @@
 	d->path[d->len - 1] = '\0'; /* no trailing slash */
 	ret =
 		write_directory(c) ||
-		write_archive_entry(d->sha1, d->path, d->baselen,
+		write_archive_entry(d->oid.hash, d->path, d->baselen,
 				    d->path + d->baselen, d->mode,
 				    d->stage, c) != READ_TREE_RECURSIVE;
 	free(d);
@@ -354,7 +354,7 @@
 	time_t archive_time;
 	struct tree *tree;
 	const struct commit *commit;
-	unsigned char sha1[20];
+	struct object_id oid;
 
 	/* Remotes are only allowed to fetch actual refs */
 	if (remote && !remote_allow_unreachable) {
@@ -362,15 +362,15 @@
 		const char *colon = strchrnul(name, ':');
 		int refnamelen = colon - name;
 
-		if (!dwim_ref(name, refnamelen, sha1, &ref))
+		if (!dwim_ref(name, refnamelen, oid.hash, &ref))
 			die("no such ref: %.*s", refnamelen, name);
 		free(ref);
 	}
 
-	if (get_sha1(name, sha1))
+	if (get_sha1(name, oid.hash))
 		die("Not a valid object name");
 
-	commit = lookup_commit_reference_gently(sha1, 1);
+	commit = lookup_commit_reference_gently(oid.hash, 1);
 	if (commit) {
 		commit_sha1 = commit->object.sha1;
 		archive_time = commit->date;
@@ -379,21 +379,21 @@
 		archive_time = time(NULL);
 	}
 
-	tree = parse_tree_indirect(sha1);
+	tree = parse_tree_indirect(oid.hash);
 	if (tree == NULL)
 		die("not a tree object");
 
 	if (prefix) {
-		unsigned char tree_sha1[20];
+		struct object_id tree_oid;
 		unsigned int mode;
 		int err;
 
 		err = get_tree_entry(tree->object.sha1, prefix,
-				     tree_sha1, &mode);
+				     tree_oid.hash, &mode);
 		if (err || !S_ISDIR(mode))
 			die("current working directory is untracked");
 
-		tree = parse_tree_indirect(tree_sha1);
+		tree = parse_tree_indirect(tree_oid.hash);
 	}
 	ar_args->tree = tree;
 	ar_args->commit_sha1 = commit_sha1;
diff --git a/bisect.c b/bisect.c
index 8c6d843..03d5cd9 100644
--- a/bisect.c
+++ b/bisect.c
@@ -15,7 +15,7 @@
 static struct sha1_array good_revs;
 static struct sha1_array skipped_revs;
 
-static unsigned char *current_bad_sha1;
+static struct object_id *current_bad_oid;
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
@@ -400,16 +400,16 @@
 	return best;
 }
 
-static int register_ref(const char *refname, const unsigned char *sha1,
+static int register_ref(const char *refname, const struct object_id *oid,
 			int flags, void *cb_data)
 {
 	if (!strcmp(refname, "bad")) {
-		current_bad_sha1 = xmalloc(20);
-		hashcpy(current_bad_sha1, sha1);
+		current_bad_oid = xmalloc(sizeof(*current_bad_oid));
+		oidcpy(current_bad_oid, oid);
 	} else if (starts_with(refname, "good-")) {
-		sha1_array_append(&good_revs, sha1);
+		sha1_array_append(&good_revs, oid->hash);
 	} else if (starts_with(refname, "skip-")) {
-		sha1_array_append(&skipped_revs, sha1);
+		sha1_array_append(&skipped_revs, oid->hash);
 	}
 
 	return 0;
@@ -564,7 +564,7 @@
 
 	for (i = 0; cur; cur = cur->next, i++) {
 		if (i == index) {
-			if (hashcmp(cur->item->object.sha1, current_bad_sha1))
+			if (hashcmp(cur->item->object.sha1, current_bad_oid->hash))
 				return cur;
 			if (previous)
 				return previous;
@@ -607,7 +607,7 @@
 
 	/* rev_argv.argv[0] will be ignored by setup_revisions */
 	argv_array_push(&rev_argv, "bisect_rev_setup");
-	argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
+	argv_array_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
 	for (i = 0; i < good_revs.nr; i++)
 		argv_array_pushf(&rev_argv, good_format,
 				 sha1_to_hex(good_revs.sha1[i]));
@@ -628,7 +628,7 @@
 }
 
 static void exit_if_skipped_commits(struct commit_list *tried,
-				    const unsigned char *bad)
+				    const struct object_id *bad)
 {
 	if (!tried)
 		return;
@@ -637,12 +637,12 @@
 	       "The first bad commit could be any of:\n");
 	print_commit_list(tried, "%s\n", "%s\n");
 	if (bad)
-		printf("%s\n", sha1_to_hex(bad));
+		printf("%s\n", oid_to_hex(bad));
 	printf("We cannot bisect more!\n");
 	exit(2);
 }
 
-static int is_expected_rev(const unsigned char *sha1)
+static int is_expected_rev(const struct object_id *oid)
 {
 	const char *filename = git_path("BISECT_EXPECTED_REV");
 	struct stat st;
@@ -658,7 +658,7 @@
 		return 0;
 
 	if (strbuf_getline(&str, fp, '\n') != EOF)
-		res = !strcmp(str.buf, sha1_to_hex(sha1));
+		res = !strcmp(str.buf, oid_to_hex(oid));
 
 	strbuf_release(&str);
 	fclose(fp);
@@ -719,7 +719,7 @@
 	struct commit **rev = xmalloc(len * sizeof(*rev));
 	int i, n = 0;
 
-	rev[n++] = get_commit_reference(current_bad_sha1);
+	rev[n++] = get_commit_reference(current_bad_oid->hash);
 	for (i = 0; i < good_revs.nr; i++)
 		rev[n++] = get_commit_reference(good_revs.sha1[i]);
 	*rev_nr = n;
@@ -729,8 +729,8 @@
 
 static void handle_bad_merge_base(void)
 {
-	if (is_expected_rev(current_bad_sha1)) {
-		char *bad_hex = sha1_to_hex(current_bad_sha1);
+	if (is_expected_rev(current_bad_oid)) {
+		char *bad_hex = oid_to_hex(current_bad_oid);
 		char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 
 		fprintf(stderr, "The merge base %s is bad.\n"
@@ -750,7 +750,7 @@
 static void handle_skipped_merge_base(const unsigned char *mb)
 {
 	char *mb_hex = sha1_to_hex(mb);
-	char *bad_hex = sha1_to_hex(current_bad_sha1);
+	char *bad_hex = sha1_to_hex(current_bad_oid->hash);
 	char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 
 	warning("the merge base between %s and [%s] "
@@ -781,7 +781,7 @@
 
 	for (; result; result = result->next) {
 		const unsigned char *mb = result->item->object.sha1;
-		if (!hashcmp(mb, current_bad_sha1)) {
+		if (!hashcmp(mb, current_bad_oid->hash)) {
 			handle_bad_merge_base();
 		} else if (0 <= sha1_array_lookup(&good_revs, mb)) {
 			continue;
@@ -838,7 +838,7 @@
 	struct stat st;
 	int fd;
 
-	if (!current_bad_sha1)
+	if (!current_bad_oid)
 		die("a bad revision is needed");
 
 	/* Check if file BISECT_ANCESTORS_OK exists. */
@@ -903,7 +903,7 @@
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	const unsigned char *bisect_rev;
-	char bisect_rev_hex[41];
+	char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
 
 	if (read_bisect_refs())
 		die("reading bisect refs failed");
@@ -927,7 +927,7 @@
 		exit_if_skipped_commits(tried, NULL);
 
 		printf("%s was both good and bad\n",
-		       sha1_to_hex(current_bad_sha1));
+		       oid_to_hex(current_bad_oid));
 		exit(1);
 	}
 
@@ -938,10 +938,10 @@
 	}
 
 	bisect_rev = revs.commits->item->object.sha1;
-	memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
+	memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
 
-	if (!hashcmp(bisect_rev, current_bad_sha1)) {
-		exit_if_skipped_commits(tried, current_bad_sha1);
+	if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
+		exit_if_skipped_commits(tried, current_bad_oid);
 		printf("%s is the first bad commit\n", bisect_rev_hex);
 		show_diff_tree(prefix, revs.commits->item);
 		/* This means the bisection process succeeded. */
diff --git a/builtin/apply.c b/builtin/apply.c
index 0769b09..54aba4e 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -208,7 +208,7 @@
 	struct patch *next;
 
 	/* three-way fallback result */
-	unsigned char threeway_stage[3][20];
+	struct object_id threeway_stage[3];
 };
 
 static void free_fragment_list(struct fragment *list)
@@ -1638,6 +1638,9 @@
 	}
 	if (oldlines || newlines)
 		return -1;
+	if (!deleted && !added)
+		return -1;
+
 	fragment->leading = leading;
 	fragment->trailing = trailing;
 
@@ -3426,11 +3429,11 @@
 	if (status) {
 		patch->conflicted_threeway = 1;
 		if (patch->is_new)
-			hashclr(patch->threeway_stage[0]);
+			oidclr(&patch->threeway_stage[0]);
 		else
-			hashcpy(patch->threeway_stage[0], pre_sha1);
-		hashcpy(patch->threeway_stage[1], our_sha1);
-		hashcpy(patch->threeway_stage[2], post_sha1);
+			hashcpy(patch->threeway_stage[0].hash, pre_sha1);
+		hashcpy(patch->threeway_stage[1].hash, our_sha1);
+		hashcpy(patch->threeway_stage[2].hash, post_sha1);
 		fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
 	} else {
 		fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
@@ -4186,14 +4189,14 @@
 
 	remove_file_from_cache(patch->new_name);
 	for (stage = 1; stage < 4; stage++) {
-		if (is_null_sha1(patch->threeway_stage[stage - 1]))
+		if (is_null_oid(&patch->threeway_stage[stage - 1]))
 			continue;
 		ce = xcalloc(1, ce_size);
 		memcpy(ce->name, patch->new_name, namelen);
 		ce->ce_mode = create_ce_mode(mode);
 		ce->ce_flags = create_ce_flags(stage);
 		ce->ce_namelen = namelen;
-		hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+		hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
 		if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 			die(_("unable to add cache entry for %s"), patch->new_name);
 	}
diff --git a/builtin/blame.c b/builtin/blame.c
index b3e948e..a22ac17 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2176,6 +2176,14 @@
 		blank_boundary = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "blame.showemail")) {
+		int *output_option = cb;
+		if (git_config_bool(var, value))
+			*output_option |= OUTPUT_SHOW_EMAIL;
+		else
+			*output_option &= ~OUTPUT_SHOW_EMAIL;
+		return 0;
+	}
 	if (!strcmp(var, "blame.date")) {
 		if (!value)
 			return config_error_nonbool(var);
@@ -2520,7 +2528,7 @@
 	unsigned int range_i;
 	long anchor;
 
-	git_config(git_blame_config, NULL);
+	git_config(git_blame_config, &output_option);
 	init_revisions(&revs, NULL);
 	revs.date_mode = blame_date_mode;
 	DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
diff --git a/builtin/branch.c b/builtin/branch.c
index a0a03fc..b42e5b6 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -123,14 +123,12 @@
 
 	if (kind == REF_LOCAL_BRANCH) {
 		struct branch *branch = branch_get(name);
+		const char *upstream = branch_get_upstream(branch, NULL);
 		unsigned char sha1[20];
 
-		if (branch &&
-		    branch->merge &&
-		    branch->merge[0] &&
-		    branch->merge[0]->dst &&
+		if (upstream &&
 		    (reference_name = reference_name_to_free =
-		     resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+		     resolve_refdup(upstream, RESOLVE_REF_READING,
 				    sha1, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(sha1);
 	}
@@ -328,7 +326,7 @@
 	return 0;
 }
 
-static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data)
 {
 	struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
 	struct ref_list *ref_list = cb->ref_list;
@@ -365,7 +363,7 @@
 
 	commit = NULL;
 	if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
-		commit = lookup_commit_reference_gently(sha1, 1);
+		commit = lookup_commit_reference_gently(oid->hash, 1);
 		if (!commit) {
 			cb->ret = error(_("branch '%s' does not point at a commit"), refname);
 			return 0;
@@ -427,25 +425,19 @@
 	int ours, theirs;
 	char *ref = NULL;
 	struct branch *branch = branch_get(branch_name);
+	const char *upstream;
 	struct strbuf fancy = STRBUF_INIT;
 	int upstream_is_gone = 0;
 	int added_decoration = 1;
 
-	switch (stat_tracking_info(branch, &ours, &theirs)) {
-	case 0:
-		/* no base */
-		return;
-	case -1:
-		/* with "gone" base */
+	if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
+		if (!upstream)
+			return;
 		upstream_is_gone = 1;
-		break;
-	default:
-		/* with base */
-		break;
 	}
 
 	if (show_upstream_ref) {
-		ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+		ref = shorten_unambiguous_ref(upstream, 0);
 		if (want_color(branch_use_color))
 			strbuf_addf(&fancy, "%s%s%s",
 					branch_get_color(BRANCH_COLOR_UPSTREAM),
@@ -771,7 +763,6 @@
 
 static int edit_branch_description(const char *branch_name)
 {
-	FILE *fp;
 	int status;
 	struct strbuf buf = STRBUF_INIT;
 	struct strbuf name = STRBUF_INIT;
@@ -784,8 +775,7 @@
 		    "  %s\n"
 		    "Lines starting with '%c' will be stripped.\n",
 		    branch_name, comment_line_char);
-	fp = fopen(git_path(edit_description), "w");
-	if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
+	if (write_file(git_path(edit_description), 0, "%s", buf.buf)) {
 		strbuf_release(&buf);
 		return error(_("could not write branch description template: %s"),
 			     strerror(errno));
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index df99df4..049a95f 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -8,14 +8,22 @@
 #include "parse-options.h"
 #include "userdiff.h"
 #include "streaming.h"
+#include "tree-walk.h"
 
-static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
+static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
+			int unknown_type)
 {
 	unsigned char sha1[20];
 	enum object_type type;
 	char *buf;
 	unsigned long size;
 	struct object_context obj_context;
+	struct object_info oi = {NULL};
+	struct strbuf sb = STRBUF_INIT;
+	unsigned flags = LOOKUP_REPLACE_OBJECT;
+
+	if (unknown_type)
+		flags |= LOOKUP_UNKNOWN_OBJECT;
 
 	if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
 		die("Not a valid object name %s", obj_name);
@@ -23,20 +31,22 @@
 	buf = NULL;
 	switch (opt) {
 	case 't':
-		type = sha1_object_info(sha1, NULL);
-		if (type > 0) {
-			printf("%s\n", typename(type));
+		oi.typename = &sb;
+		if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+			die("git cat-file: could not get object info");
+		if (sb.len) {
+			printf("%s\n", sb.buf);
+			strbuf_release(&sb);
 			return 0;
 		}
 		break;
 
 	case 's':
-		type = sha1_object_info(sha1, &size);
-		if (type > 0) {
-			printf("%lu\n", size);
-			return 0;
-		}
-		break;
+		oi.sizep = &size;
+		if (sha1_object_info_extended(sha1, &oi, flags) < 0)
+			die("git cat-file: could not get object info");
+		printf("%lu\n", size);
+		return 0;
 
 	case 'e':
 		return !has_sha1_file(sha1);
@@ -224,6 +234,7 @@
 
 struct batch_options {
 	int enabled;
+	int follow_symlinks;
 	int print_contents;
 	const char *format;
 };
@@ -232,12 +243,44 @@
 			    struct expand_data *data)
 {
 	struct strbuf buf = STRBUF_INIT;
+	struct object_context ctx;
+	int flags = opt->follow_symlinks ? GET_SHA1_FOLLOW_SYMLINKS : 0;
+	enum follow_symlinks_result result;
 
 	if (!obj_name)
 	   return 1;
 
-	if (get_sha1(obj_name, data->sha1)) {
-		printf("%s missing\n", obj_name);
+	result = get_sha1_with_context(obj_name, flags, data->sha1, &ctx);
+	if (result != FOUND) {
+		switch (result) {
+		case MISSING_OBJECT:
+			printf("%s missing\n", obj_name);
+			break;
+		case DANGLING_SYMLINK:
+			printf("dangling %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		case SYMLINK_LOOP:
+			printf("loop %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		case NOT_DIR:
+			printf("notdir %"PRIuMAX"\n%s\n",
+			       (uintmax_t)strlen(obj_name), obj_name);
+			break;
+		default:
+			die("BUG: unknown get_sha1_with_context result %d\n",
+			       result);
+			break;
+		}
+		fflush(stdout);
+		return 0;
+	}
+
+	if (ctx.mode == 0) {
+		printf("symlink %"PRIuMAX"\n%s\n",
+		       (uintmax_t)ctx.symlink_path.len,
+		       ctx.symlink_path.buf);
 		fflush(stdout);
 		return 0;
 	}
@@ -323,8 +366,8 @@
 }
 
 static const char * const cat_file_usage[] = {
-	N_("git cat-file (-t | -s | -e | -p | <type> | --textconv) <object>"),
-	N_("git cat-file (--batch | --batch-check) < <list-of-objects>"),
+	N_("git cat-file (-t [--allow-unknown-type]|-s [--allow-unknown-type]|-e|-p|<type>|--textconv) <object>"),
+	N_("git cat-file (--batch | --batch-check) [--follow-symlinks] < <list-of-objects>"),
 	NULL
 };
 
@@ -342,9 +385,8 @@
 {
 	struct batch_options *bo = opt->value;
 
-	if (unset) {
-		memset(bo, 0, sizeof(*bo));
-		return 0;
+	if (bo->enabled) {
+		return 1;
 	}
 
 	bo->enabled = 1;
@@ -359,30 +401,32 @@
 	int opt = 0;
 	const char *exp_type = NULL, *obj_name = NULL;
 	struct batch_options batch = {0};
+	int unknown_type = 0;
 
 	const struct option options[] = {
 		OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
-		OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'),
-		OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'),
-		OPT_SET_INT('e', NULL, &opt,
+		OPT_CMDMODE('t', NULL, &opt, N_("show object type"), 't'),
+		OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
+		OPT_CMDMODE('e', NULL, &opt,
 			    N_("exit with zero when there's no error"), 'e'),
-		OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
-		OPT_SET_INT(0, "textconv", &opt,
+		OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
+		OPT_CMDMODE(0, "textconv", &opt,
 			    N_("for blob objects, run textconv on object's content"), 'c'),
+		OPT_BOOL( 0, "allow-unknown-type", &unknown_type,
+			  N_("allow -s and -t to work with broken/corrupt objects")),
 		{ OPTION_CALLBACK, 0, "batch", &batch, "format",
 			N_("show info and content of objects fed from the standard input"),
 			PARSE_OPT_OPTARG, batch_option_callback },
 		{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
 			N_("show info about objects fed from the standard input"),
 			PARSE_OPT_OPTARG, batch_option_callback },
+		OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
+			 N_("follow in-tree symlinks (used with --batch or --batch-check)")),
 		OPT_END()
 	};
 
 	git_config(git_cat_file_config, NULL);
 
-	if (argc != 3 && argc != 2)
-		usage_with_options(cat_file_usage, options);
-
 	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
 
 	if (opt) {
@@ -402,8 +446,14 @@
 		usage_with_options(cat_file_usage, options);
 	}
 
+	if (batch.follow_symlinks && !batch.enabled) {
+		usage_with_options(cat_file_usage, options);
+	}
+
 	if (batch.enabled)
 		return batch_objects(&batch);
 
-	return cat_one_file(opt, exp_type, obj_name);
+	if (unknown_type && opt != 't' && opt != 's')
+		die("git cat-file --allow-unknown-type: use with -s or -t");
+	return cat_one_file(opt, exp_type, obj_name, unknown_type);
 }
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 9ca2da1..8028c37 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -241,7 +241,7 @@
 	/* Check out named files first */
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
-		const char *p;
+		char *p;
 
 		if (all)
 			die("git checkout-index: don't mix '--all' and explicit filenames");
@@ -249,8 +249,7 @@
 			die("git checkout-index: don't mix '--stdin' and explicit filenames");
 		p = prefix_path(prefix, prefix_length, arg);
 		checkout_file(p, prefix);
-		if (p < arg || p > arg + strlen(arg))
-			free((char *)p);
+		free(p);
 	}
 
 	if (read_from_stdin) {
@@ -260,7 +259,7 @@
 			die("git checkout-index: don't mix '--all' and '--stdin'");
 
 		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-			const char *p;
+			char *p;
 			if (line_termination && buf.buf[0] == '"') {
 				strbuf_reset(&nbuf);
 				if (unquote_c_style(&nbuf, buf.buf, NULL))
@@ -269,8 +268,7 @@
 			}
 			p = prefix_path(prefix, prefix_length, buf.buf);
 			checkout_file(p, prefix);
-			if (p < buf.buf || p > buf.buf + buf.len)
-				free((char *)p);
+			free(p);
 		}
 		strbuf_release(&nbuf);
 		strbuf_release(&buf);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3e141fc..9b49f0e 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -20,6 +20,7 @@
 #include "resolve-undo.h"
 #include "submodule.h"
 #include "argv-array.h"
+#include "sigchain.h"
 
 static const char * const checkout_usage[] = {
 	N_("git checkout [<options>] <branch>"),
@@ -36,6 +37,7 @@
 	int writeout_stage;
 	int overwrite_ignore;
 	int ignore_skipworktree;
+	int ignore_other_worktrees;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -48,6 +50,10 @@
 	const char *prefix;
 	struct pathspec pathspec;
 	struct tree *source_tree;
+
+	const char *new_worktree;
+	const char **saved_argv;
+	int new_worktree_mode;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -267,6 +273,9 @@
 		die(_("Cannot update paths and switch to branch '%s' at the same time."),
 		    opts->new_branch);
 
+	if (opts->new_worktree)
+		die(_("'%s' cannot be used with updating paths"), "--to");
+
 	if (opts->patch_mode)
 		return run_add_interactive(revision, "--patch=checkout",
 					   &opts->pathspec);
@@ -441,6 +450,11 @@
 	const char *name; /* The short name used */
 	const char *path; /* The full name of a real branch */
 	struct commit *commit; /* The named commit */
+	/*
+	 * if not null the branch is detached because it's already
+	 * checked out in this checkout
+	 */
+	char *checkout;
 };
 
 static void setup_branch_path(struct branch_info *branch)
@@ -502,7 +516,7 @@
 			topts.dir->flags |= DIR_SHOW_IGNORED;
 			setup_standard_excludes(topts.dir);
 		}
-		tree = parse_tree_indirect(old->commit ?
+		tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ?
 					   old->commit->object.sha1 :
 					   EMPTY_TREE_SHA1_BIN);
 		init_tree_desc(&trees[0], tree->buffer, tree->size);
@@ -606,18 +620,21 @@
 		if (opts->new_orphan_branch) {
 			if (opts->new_branch_log && !log_all_ref_updates) {
 				int temp;
-				char log_file[PATH_MAX];
-				char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+				struct strbuf log_file = STRBUF_INIT;
+				int ret;
+				const char *ref_name;
 
+				ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
 				temp = log_all_ref_updates;
 				log_all_ref_updates = 1;
-				if (log_ref_setup(ref_name, log_file, sizeof(log_file))) {
+				ret = log_ref_setup(ref_name, &log_file);
+				log_all_ref_updates = temp;
+				strbuf_release(&log_file);
+				if (ret) {
 					fprintf(stderr, _("Can not do reflog for '%s'\n"),
 					    opts->new_orphan_branch);
-					log_all_ref_updates = temp;
 					return;
 				}
-				log_all_ref_updates = temp;
 			}
 		}
 		else
@@ -685,10 +702,10 @@
 }
 
 static int add_pending_uninteresting_ref(const char *refname,
-					 const unsigned char *sha1,
+					 const struct object_id *oid,
 					 int flags, void *cb_data)
 {
-	add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
+	add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
 	return 0;
 }
 
@@ -743,10 +760,17 @@
 
 	if (advice_detached_head)
 		fprintf(stderr,
-			_(
+			Q_(
+			/* The singular version */
+			"If you want to keep it by creating a new branch, "
+			"this may be a good time\nto do so with:\n\n"
+			" git branch <new-branch-name> %s\n\n",
+			/* The plural version */
 			"If you want to keep them by creating a new branch, "
 			"this may be a good time\nto do so with:\n\n"
-			" git branch <new-branch-name> %s\n\n"),
+			" git branch <new-branch-name> %s\n\n",
+			/* Give ngettext() the count */
+			lost),
 			find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 }
 
@@ -815,7 +839,8 @@
 		return ret;
 	}
 
-	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
+	if (!opts->quiet && !old.path && old.commit &&
+	    new->commit != old.commit && !opts->new_worktree_mode)
 		orphaned_commit_warning(old.commit, new->commit);
 
 	update_refs_for_switch(opts, &old, new);
@@ -825,6 +850,138 @@
 	return ret || writeout_error;
 }
 
+static char *junk_work_tree;
+static char *junk_git_dir;
+static int is_junk;
+static pid_t junk_pid;
+
+static void remove_junk(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	if (!is_junk || getpid() != junk_pid)
+		return;
+	if (junk_git_dir) {
+		strbuf_addstr(&sb, junk_git_dir);
+		remove_dir_recursively(&sb, 0);
+		strbuf_reset(&sb);
+	}
+	if (junk_work_tree) {
+		strbuf_addstr(&sb, junk_work_tree);
+		remove_dir_recursively(&sb, 0);
+	}
+	strbuf_release(&sb);
+}
+
+static void remove_junk_on_signal(int signo)
+{
+	remove_junk();
+	sigchain_pop(signo);
+	raise(signo);
+}
+
+static int prepare_linked_checkout(const struct checkout_opts *opts,
+				   struct branch_info *new)
+{
+	struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
+	struct strbuf sb = STRBUF_INIT;
+	const char *path = opts->new_worktree, *name;
+	struct stat st;
+	struct child_process cp;
+	int counter = 0, len, ret;
+
+	if (!new->commit)
+		die(_("no branch specified"));
+	if (file_exists(path) && !is_empty_dir(path))
+		die(_("'%s' already exists"), path);
+
+	len = strlen(path);
+	while (len && is_dir_sep(path[len - 1]))
+		len--;
+
+	for (name = path + len - 1; name > path; name--)
+		if (is_dir_sep(*name)) {
+			name++;
+			break;
+		}
+	strbuf_addstr(&sb_repo,
+		      git_path("worktrees/%.*s", (int)(path + len - name), name));
+	len = sb_repo.len;
+	if (safe_create_leading_directories_const(sb_repo.buf))
+		die_errno(_("could not create leading directories of '%s'"),
+			  sb_repo.buf);
+	while (!stat(sb_repo.buf, &st)) {
+		counter++;
+		strbuf_setlen(&sb_repo, len);
+		strbuf_addf(&sb_repo, "%d", counter);
+	}
+	name = strrchr(sb_repo.buf, '/') + 1;
+
+	junk_pid = getpid();
+	atexit(remove_junk);
+	sigchain_push_common(remove_junk_on_signal);
+
+	if (mkdir(sb_repo.buf, 0777))
+		die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+	junk_git_dir = xstrdup(sb_repo.buf);
+	is_junk = 1;
+
+	/*
+	 * lock the incomplete repo so prune won't delete it, unlock
+	 * after the preparation is over.
+	 */
+	strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+	write_file(sb.buf, 1, "initializing\n");
+
+	strbuf_addf(&sb_git, "%s/.git", path);
+	if (safe_create_leading_directories_const(sb_git.buf))
+		die_errno(_("could not create leading directories of '%s'"),
+			  sb_git.buf);
+	junk_work_tree = xstrdup(path);
+
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+	write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
+	write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
+		   real_path(get_git_common_dir()), name);
+	/*
+	 * This is to keep resolve_ref() happy. We need a valid HEAD
+	 * or is_git_directory() will reject the directory. Any valid
+	 * value would do because this value will be ignored and
+	 * replaced at the next (real) checkout.
+	 */
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+	write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
+	write_file(sb.buf, 1, "../..\n");
+
+	if (!opts->quiet)
+		fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
+
+	setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
+	setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
+	setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = opts->saved_argv;
+	ret = run_command(&cp);
+	if (!ret) {
+		is_junk = 0;
+		free(junk_work_tree);
+		free(junk_git_dir);
+		junk_work_tree = NULL;
+		junk_git_dir = NULL;
+	}
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+	unlink_or_warn(sb.buf);
+	strbuf_release(&sb);
+	strbuf_release(&sb_repo);
+	strbuf_release(&sb_git);
+	return ret;
+}
+
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
 	if (!strcmp(var, "diff.ignoresubmodules")) {
@@ -880,13 +1037,80 @@
 	return NULL;
 }
 
+static void check_linked_checkout(struct branch_info *new, const char *id)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	struct strbuf gitdir = STRBUF_INIT;
+	const char *start, *end;
+
+	if (id)
+		strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+	else
+		strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+
+	if (strbuf_read_file(&sb, path.buf, 0) < 0 ||
+	    !skip_prefix(sb.buf, "ref:", &start))
+		goto done;
+	while (isspace(*start))
+		start++;
+	end = start;
+	while (*end && !isspace(*end))
+		end++;
+	if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0')
+		goto done;
+	if (id) {
+		strbuf_reset(&path);
+		strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id);
+		if (strbuf_read_file(&gitdir, path.buf, 0) <= 0)
+			goto done;
+		strbuf_rtrim(&gitdir);
+	} else
+		strbuf_addstr(&gitdir, get_git_common_dir());
+	die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf);
+done:
+	strbuf_release(&path);
+	strbuf_release(&sb);
+	strbuf_release(&gitdir);
+}
+
+static void check_linked_checkouts(struct branch_info *new)
+{
+	struct strbuf path = STRBUF_INIT;
+	DIR *dir;
+	struct dirent *d;
+
+	strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
+	if ((dir = opendir(path.buf)) == NULL) {
+		strbuf_release(&path);
+		return;
+	}
+
+	/*
+	 * $GIT_COMMON_DIR/HEAD is practically outside
+	 * $GIT_DIR so resolve_ref_unsafe() won't work (it
+	 * uses git_path). Parse the ref ourselves.
+	 */
+	check_linked_checkout(new, NULL);
+
+	while ((d = readdir(dir)) != NULL) {
+		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			continue;
+		check_linked_checkout(new, d->d_name);
+	}
+	strbuf_release(&path);
+	closedir(dir);
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
 				int dwim_new_local_branch_ok,
 				struct branch_info *new,
-				struct tree **source_tree,
-				unsigned char rev[20],
-				const char **new_branch)
+				struct checkout_opts *opts,
+				unsigned char rev[20])
 {
+	struct tree **source_tree = &opts->source_tree;
+	const char **new_branch = &opts->new_branch;
+	int force_detach = opts->force_detach;
 	int argcount = 0;
 	unsigned char branch_rev[20];
 	const char *arg;
@@ -1007,6 +1231,17 @@
 	else
 		new->path = NULL; /* not an existing branch */
 
+	if (new->path && !force_detach && !*new_branch) {
+		unsigned char sha1[20];
+		int flag;
+		char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+		if (head_ref &&
+		    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
+		    !opts->ignore_other_worktrees)
+			check_linked_checkouts(new);
+		free(head_ref);
+	}
+
 	new->commit = lookup_commit_reference_gently(rev, 1);
 	if (!new->commit) {
 		/* not a commit */
@@ -1086,6 +1321,9 @@
 		die(_("Cannot switch branch to a non-commit '%s'"),
 		    new->name);
 
+	if (opts->new_worktree)
+		return prepare_linked_checkout(opts, new);
+
 	if (!new->commit && opts->new_branch) {
 		unsigned char rev[20];
 		int flag;
@@ -1128,6 +1366,10 @@
 			 N_("do not limit pathspecs to sparse entries only")),
 		OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
 				N_("second guess 'git checkout <no-such-branch>'")),
+		OPT_FILENAME(0, "to", &opts.new_worktree,
+			   N_("check a branch out in a separate working directory")),
+		OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
+			 N_("do not check if another worktree is holding the given ref")),
 		OPT_END(),
 	};
 
@@ -1136,6 +1378,9 @@
 	opts.overwrite_ignore = 1;
 	opts.prefix = prefix;
 
+	opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
+	memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
+
 	gitmodules_config();
 	git_config(git_checkout_config, &opts);
 
@@ -1144,6 +1389,14 @@
 	argc = parse_options(argc, argv, prefix, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
+	/* recursive execution from checkout_new_worktree() */
+	opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
+	if (opts.new_worktree_mode)
+		opts.new_worktree = NULL;
+
+	if (!opts.new_worktree)
+		setup_work_tree();
+
 	if (conflict_style) {
 		opts.merge = 1; /* implied */
 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
@@ -1197,8 +1450,7 @@
 			opts.track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts.new_branch;
 		int n = parse_branchname_arg(argc, argv, dwim_ok,
-					     &new, &opts.source_tree,
-					     rev, &opts.new_branch);
+					     &new, &opts, rev);
 		argv += n;
 		argc -= n;
 	}
diff --git a/builtin/clone.c b/builtin/clone.c
index e18839d..a72ff7e 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -277,16 +277,17 @@
 	struct strbuf line = STRBUF_INIT;
 
 	while (strbuf_getline(&line, in, '\n') != EOF) {
-		char *abs_path, abs_buf[PATH_MAX];
+		char *abs_path;
 		if (!line.len || line.buf[0] == '#')
 			continue;
 		if (is_absolute_path(line.buf)) {
 			add_to_alternates_file(line.buf);
 			continue;
 		}
-		abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
-		normalize_path_copy(abs_buf, abs_path);
-		add_to_alternates_file(abs_buf);
+		abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+		normalize_path_copy(abs_path, abs_path);
+		add_to_alternates_file(abs_path);
+		free(abs_path);
 	}
 	strbuf_release(&line);
 	fclose(in);
diff --git a/builtin/commit.c b/builtin/commit.c
index c2ebea4..254477f 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -170,7 +170,7 @@
 		whence = FROM_MERGE;
 	else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
 		whence = FROM_CHERRY_PICK;
-		if (file_exists(git_path("sequencer")))
+		if (file_exists(git_path(SEQ_DIR)))
 			sequencer_in_use = 1;
 	}
 	else
@@ -1366,13 +1366,14 @@
 	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
 
 	fd = hold_locked_index(&index_lock, 0);
-	if (0 <= fd)
-		update_index_if_able(&the_index, &index_lock);
 
 	s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
 	s.ignore_submodule_arg = ignore_submodule_arg;
 	wt_status_collect(&s);
 
+	if (0 <= fd)
+		update_index_if_able(&the_index, &index_lock);
+
 	if (s.relative_paths)
 		s.prefix = prefix;
 
diff --git a/builtin/config.c b/builtin/config.c
index a58f99c..7188405 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -193,7 +193,7 @@
 
 		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
 		if (regcomp(key_regexp, key, REG_EXTENDED)) {
-			fprintf(stderr, "Invalid key pattern: %s\n", key_);
+			error("invalid key pattern: %s", key_);
 			free(key_regexp);
 			key_regexp = NULL;
 			ret = CONFIG_INVALID_PATTERN;
@@ -214,7 +214,7 @@
 
 		regexp = (regex_t*)xmalloc(sizeof(regex_t));
 		if (regcomp(regexp, regex_, REG_EXTENDED)) {
-			fprintf(stderr, "Invalid pattern: %s\n", regex_);
+			error("invalid pattern: %s", regex_);
 			free(regexp);
 			regexp = NULL;
 			ret = CONFIG_INVALID_PATTERN;
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index e47ef0b..ad0c799 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -70,8 +70,10 @@
 	/* we do not take arguments other than flags for now */
 	if (argc)
 		usage_with_options(count_objects_usage, opts);
-	if (verbose)
+	if (verbose) {
 		report_garbage = real_report_garbage;
+		report_linked_checkout_garbage();
+	}
 
 	for_each_loose_file_in_objdir(get_object_directory(),
 				      count_loose, count_cruft, NULL, NULL);
diff --git a/builtin/describe.c b/builtin/describe.c
index e00a75b..a36c829 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -119,10 +119,10 @@
 	}
 }
 
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
 {
 	int is_tag = starts_with(path, "refs/tags/");
-	unsigned char peeled[20];
+	struct object_id peeled;
 	int is_annotated, prio;
 
 	/* Reject anything outside refs/tags/ unless --all */
@@ -134,10 +134,10 @@
 		return 0;
 
 	/* Is it annotated? */
-	if (!peel_ref(path, peeled)) {
-		is_annotated = !!hashcmp(sha1, peeled);
+	if (!peel_ref(path, peeled.hash)) {
+		is_annotated = !!oidcmp(oid, &peeled);
 	} else {
-		hashcpy(peeled, sha1);
+		oidcpy(&peeled, oid);
 		is_annotated = 0;
 	}
 
@@ -154,7 +154,7 @@
 	else
 		prio = 0;
 
-	add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1);
+	add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
 	return 0;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f951265..8d5b2db 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -179,13 +179,15 @@
 	}
 }
 
-static int add_existing(const char *refname, const unsigned char *sha1,
+static int add_existing(const char *refname, const struct object_id *oid,
 			int flag, void *cbdata)
 {
 	struct string_list *list = (struct string_list *)cbdata;
 	struct string_list_item *item = string_list_insert(list, refname);
-	item->util = xmalloc(20);
-	hashcpy(item->util, sha1);
+	struct object_id *old_oid = xmalloc(sizeof(*old_oid));
+
+	oidcpy(old_oid, oid);
+	item->util = old_oid;
 	return 0;
 }
 
@@ -588,7 +590,8 @@
 	struct strbuf note = STRBUF_INIT;
 	const char *what, *kind;
 	struct ref *rm;
-	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+	char *url;
+	const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 	int want_status;
 
 	fp = fopen(filename, "a");
@@ -822,7 +825,7 @@
 
 static int truncate_fetch_head(void)
 {
-	char *filename = git_path("FETCH_HEAD");
+	const char *filename = git_path("FETCH_HEAD");
 	FILE *fp = fopen(filename, "w");
 
 	if (!fp)
@@ -912,9 +915,10 @@
 			struct string_list_item *peer_item =
 				string_list_lookup(&existing_refs,
 						   rm->peer_ref->name);
-			if (peer_item)
-				hashcpy(rm->peer_ref->old_sha1,
-					peer_item->util);
+			if (peer_item) {
+				struct object_id *old_oid = peer_item->util;
+				hashcpy(rm->peer_ref->old_sha1, old_oid->hash);
+			}
 		}
 	}
 
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 83f9cf9..cb7db23 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -74,6 +74,7 @@
 	{ "contents:body" },
 	{ "contents:signature" },
 	{ "upstream" },
+	{ "push" },
 	{ "symref" },
 	{ "flag" },
 	{ "HEAD" },
@@ -659,15 +660,26 @@
 		else if (starts_with(name, "symref"))
 			refname = ref->symref ? ref->symref : "";
 		else if (starts_with(name, "upstream")) {
+			const char *branch_name;
 			/* only local branches may have an upstream */
-			if (!starts_with(ref->refname, "refs/heads/"))
+			if (!skip_prefix(ref->refname, "refs/heads/",
+					 &branch_name))
 				continue;
-			branch = branch_get(ref->refname + 11);
+			branch = branch_get(branch_name);
 
-			if (!branch || !branch->merge || !branch->merge[0] ||
-			    !branch->merge[0]->dst)
+			refname = branch_get_upstream(branch, NULL);
+			if (!refname)
 				continue;
-			refname = branch->merge[0]->dst;
+		} else if (starts_with(name, "push")) {
+			const char *branch_name;
+			if (!skip_prefix(ref->refname, "refs/heads/",
+					 &branch_name))
+				continue;
+			branch = branch_get(branch_name);
+
+			refname = branch_get_push(branch, NULL);
+			if (!refname)
+				continue;
 		} else if (starts_with(name, "color:")) {
 			char color[COLOR_MAXLEN] = "";
 
@@ -713,11 +725,12 @@
 				refname = shorten_unambiguous_ref(refname,
 						      warn_ambiguous_refs);
 			else if (!strcmp(formatp, "track") &&
-				 starts_with(name, "upstream")) {
+				 (starts_with(name, "upstream") ||
+				  starts_with(name, "push"))) {
 				char buf[40];
 
 				if (stat_tracking_info(branch, &num_ours,
-						       &num_theirs) != 1)
+						       &num_theirs, NULL))
 					continue;
 
 				if (!num_ours && !num_theirs)
@@ -735,11 +748,12 @@
 				}
 				continue;
 			} else if (!strcmp(formatp, "trackshort") &&
-				   starts_with(name, "upstream")) {
+				   (starts_with(name, "upstream") ||
+				    starts_with(name, "push"))) {
 				assert(branch);
 
 				if (stat_tracking_info(branch, &num_ours,
-							&num_theirs) != 1)
+							&num_theirs, NULL))
 					continue;
 
 				if (!num_ours && !num_theirs)
@@ -840,7 +854,8 @@
  * A call-back given to for_each_ref().  Filter refs and keep them for
  * later object processing.
  */
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int grab_single_ref(const char *refname, const struct object_id *oid,
+			   int flag, void *cb_data)
 {
 	struct grab_ref_cbdata *cb = cb_data;
 	struct refinfo *ref;
@@ -851,6 +866,11 @@
 		  return 0;
 	}
 
+	if (flag & REF_ISBROKEN) {
+		  warning("ignoring broken ref %s", refname);
+		  return 0;
+	}
+
 	if (*cb->grab_pattern) {
 		const char **pattern;
 		int namelen = strlen(refname);
@@ -878,7 +898,7 @@
 	 */
 	ref = xcalloc(1, sizeof(*ref));
 	ref->refname = xstrdup(refname);
-	hashcpy(ref->objectname, sha1);
+	hashcpy(ref->objectname, oid->hash);
 	ref->flag = flag;
 
 	cnt = cb->grab_cnt;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 0c75786..2679793 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -25,7 +25,7 @@
 static int check_full = 1;
 static int check_strict;
 static int keep_cache_objects;
-static unsigned char head_sha1[20];
+static struct object_id head_oid;
 static const char *head_points_at;
 static int errors_found;
 static int write_lost_and_found;
@@ -225,12 +225,12 @@
 			printf("dangling %s %s\n", typename(obj->type),
 			       sha1_to_hex(obj->sha1));
 		if (write_lost_and_found) {
-			char *filename = git_path("lost-found/%s/%s",
+			const char *filename = git_path("lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				sha1_to_hex(obj->sha1));
 			FILE *f;
 
-			if (safe_create_leading_directories(filename)) {
+			if (safe_create_leading_directories_const(filename)) {
 				error("Could not create lost-found");
 				return;
 			}
@@ -451,44 +451,52 @@
 
 static int default_refs;
 
+static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
+{
+	struct object *obj;
+
+	if (!is_null_sha1(sha1)) {
+		obj = lookup_object(sha1);
+		if (obj) {
+			obj->used = 1;
+			mark_object_reachable(obj);
+		} else {
+			error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1));
+			errors_found |= ERROR_REACHABLE;
+		}
+	}
+}
+
 static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 		const char *email, unsigned long timestamp, int tz,
 		const char *message, void *cb_data)
 {
-	struct object *obj;
+	const char *refname = cb_data;
 
 	if (verbose)
 		fprintf(stderr, "Checking reflog %s->%s\n",
 			sha1_to_hex(osha1), sha1_to_hex(nsha1));
 
-	if (!is_null_sha1(osha1)) {
-		obj = lookup_object(osha1);
-		if (obj) {
-			obj->used = 1;
-			mark_object_reachable(obj);
-		}
-	}
-	obj = lookup_object(nsha1);
-	if (obj) {
-		obj->used = 1;
-		mark_object_reachable(obj);
-	}
+	fsck_handle_reflog_sha1(refname, osha1);
+	fsck_handle_reflog_sha1(refname, nsha1);
 	return 0;
 }
 
-static int fsck_handle_reflog(const char *logname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
+			      int flag, void *cb_data)
 {
-	for_each_reflog_ent(logname, fsck_handle_reflog_ent, NULL);
+	for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);
 	return 0;
 }
 
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int fsck_handle_ref(const char *refname, const struct object_id *oid,
+			   int flag, void *cb_data)
 {
 	struct object *obj;
 
-	obj = parse_object(sha1);
+	obj = parse_object(oid->hash);
 	if (!obj) {
-		error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
+		error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
 		errors_found |= ERROR_REACHABLE;
 		/* We'll continue with the rest despite the error.. */
 		return 0;
@@ -504,8 +512,8 @@
 
 static void get_default_heads(void)
 {
-	if (head_points_at && !is_null_sha1(head_sha1))
-		fsck_handle_ref("HEAD", head_sha1, 0, NULL);
+	if (head_points_at && !is_null_oid(&head_oid))
+		fsck_handle_ref("HEAD", &head_oid, 0, NULL);
 	for_each_rawref(fsck_handle_ref, NULL);
 	if (include_reflogs)
 		for_each_reflog(fsck_handle_reflog, NULL);
@@ -556,7 +564,7 @@
 	if (verbose)
 		fprintf(stderr, "Checking HEAD link\n");
 
-	head_points_at = resolve_ref_unsafe("HEAD", 0, head_sha1, &flag);
+	head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag);
 	if (!head_points_at)
 		return error("Invalid HEAD");
 	if (!strcmp(head_points_at, "HEAD"))
@@ -565,7 +573,7 @@
 	else if (!starts_with(head_points_at, "refs/heads/"))
 		return error("HEAD points to something strange (%s)",
 			     head_points_at);
-	if (is_null_sha1(head_sha1)) {
+	if (is_null_oid(&head_oid)) {
 		if (null_is_error)
 			return error("HEAD: detached HEAD points at nothing");
 		fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
diff --git a/builtin/gc.c b/builtin/gc.c
index 5c634af..36fe333 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,11 +33,13 @@
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
 static const char *prune_expire = "2.weeks.ago";
+static const char *prune_worktrees_expire = "3.months.ago";
 
 static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
 static struct argv_array reflog = ARGV_ARRAY_INIT;
 static struct argv_array repack = ARGV_ARRAY_INIT;
 static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array prune_worktrees = ARGV_ARRAY_INIT;
 static struct argv_array rerere = ARGV_ARRAY_INIT;
 
 static char *pidfile;
@@ -55,6 +57,17 @@
 	raise(signo);
 }
 
+static void git_config_date_string(const char *key, const char **output)
+{
+	if (git_config_get_string_const(key, output))
+		return;
+	if (strcmp(*output, "now")) {
+		unsigned long now = approxidate("now");
+		if (approxidate(*output) >= now)
+			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
+	}
+}
+
 static void gc_config(void)
 {
 	const char *value;
@@ -71,16 +84,8 @@
 	git_config_get_int("gc.auto", &gc_auto_threshold);
 	git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
 	git_config_get_bool("gc.autodetach", &detach_auto);
-
-	if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) {
-		if (strcmp(prune_expire, "now")) {
-			unsigned long now = approxidate("now");
-			if (approxidate(prune_expire) >= now) {
-				git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"),
-						prune_expire);
-			}
-		}
-	}
+	git_config_date_string("gc.pruneexpire", &prune_expire);
+	git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire);
 	git_config(git_default_config, NULL);
 }
 
@@ -287,7 +292,8 @@
 	argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
 	argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
 	argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
-	argv_array_pushl(&prune, "prune", "--expire", NULL );
+	argv_array_pushl(&prune, "prune", "--expire", NULL);
+	argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL);
 	argv_array_pushl(&rerere, "rerere", "gc", NULL);
 
 	gc_config();
@@ -357,6 +363,12 @@
 			return error(FAILED_RUN, prune.argv[0]);
 	}
 
+	if (prune_worktrees_expire) {
+		argv_array_push(&prune_worktrees, prune_worktrees_expire);
+		if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
+			return error(FAILED_RUN, prune_worktrees.argv[0]);
+	}
+
 	if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
 		return error(FAILED_RUN, rerere.argv[0]);
 
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index cf654df..f07bc66 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -18,16 +18,14 @@
 struct object_entry {
 	struct pack_idx_entry idx;
 	unsigned long size;
-	unsigned int hdr_size;
-	enum object_type type;
-	enum object_type real_type;
-	unsigned delta_depth;
-	int base_object_no;
+	unsigned char hdr_size;
+	signed char type;
+	signed char real_type;
 };
 
-union delta_base {
-	unsigned char sha1[20];
-	off_t offset;
+struct object_stat {
+	unsigned delta_depth;
+	int base_object_no;
 };
 
 struct base_data {
@@ -49,25 +47,28 @@
 	int pack_fd;
 };
 
-/*
- * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
- * to memcmp() only the first 20 bytes.
- */
-#define UNION_BASE_SZ	20
-
 #define FLAG_LINK (1u<<20)
 #define FLAG_CHECKED (1u<<21)
 
-struct delta_entry {
-	union delta_base base;
+struct ofs_delta_entry {
+	off_t offset;
+	int obj_no;
+};
+
+struct ref_delta_entry {
+	unsigned char sha1[20];
 	int obj_no;
 };
 
 static struct object_entry *objects;
-static struct delta_entry *deltas;
+static struct object_stat *obj_stat;
+static struct ofs_delta_entry *ofs_deltas;
+static struct ref_delta_entry *ref_deltas;
 static struct thread_local nothread_data;
 static int nr_objects;
-static int nr_deltas;
+static int nr_ofs_deltas;
+static int nr_ref_deltas;
+static int ref_deltas_alloc;
 static int nr_resolved_deltas;
 static int nr_threads;
 
@@ -476,7 +477,8 @@
 }
 
 static void *unpack_raw_entry(struct object_entry *obj,
-			      union delta_base *delta_base,
+			      off_t *ofs_offset,
+			      unsigned char *ref_sha1,
 			      unsigned char *sha1)
 {
 	unsigned char *p;
@@ -505,11 +507,10 @@
 
 	switch (obj->type) {
 	case OBJ_REF_DELTA:
-		hashcpy(delta_base->sha1, fill(20));
+		hashcpy(ref_sha1, fill(20));
 		use(20);
 		break;
 	case OBJ_OFS_DELTA:
-		memset(delta_base, 0, sizeof(*delta_base));
 		p = fill(1);
 		c = *p;
 		use(1);
@@ -523,8 +524,8 @@
 			use(1);
 			base_offset = (base_offset << 7) + (c & 127);
 		}
-		delta_base->offset = obj->idx.offset - base_offset;
-		if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
+		*ofs_offset = obj->idx.offset - base_offset;
+		if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset)
 			bad_object(obj->idx.offset, _("delta base offset is out of bound"));
 		break;
 	case OBJ_COMMIT:
@@ -608,55 +609,110 @@
 	return unpack_data(obj, NULL, NULL);
 }
 
-static int compare_delta_bases(const union delta_base *base1,
-			       const union delta_base *base2,
-			       enum object_type type1,
-			       enum object_type type2)
+static int compare_ofs_delta_bases(off_t offset1, off_t offset2,
+				   enum object_type type1,
+				   enum object_type type2)
 {
 	int cmp = type1 - type2;
 	if (cmp)
 		return cmp;
-	return memcmp(base1, base2, UNION_BASE_SZ);
+	return offset1 < offset2 ? -1 :
+	       offset1 > offset2 ?  1 :
+	       0;
 }
 
-static int find_delta(const union delta_base *base, enum object_type type)
+static int find_ofs_delta(const off_t offset, enum object_type type)
 {
-	int first = 0, last = nr_deltas;
+	int first = 0, last = nr_ofs_deltas;
 
-        while (first < last) {
-                int next = (first + last) / 2;
-                struct delta_entry *delta = &deltas[next];
-                int cmp;
+	while (first < last) {
+		int next = (first + last) / 2;
+		struct ofs_delta_entry *delta = &ofs_deltas[next];
+		int cmp;
 
-		cmp = compare_delta_bases(base, &delta->base,
-					  type, objects[delta->obj_no].type);
-                if (!cmp)
-                        return next;
-                if (cmp < 0) {
-                        last = next;
-                        continue;
-                }
-                first = next+1;
-        }
-        return -first-1;
+		cmp = compare_ofs_delta_bases(offset, delta->offset,
+					      type, objects[delta->obj_no].type);
+		if (!cmp)
+			return next;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	return -first-1;
 }
 
-static void find_delta_children(const union delta_base *base,
-				int *first_index, int *last_index,
-				enum object_type type)
+static void find_ofs_delta_children(off_t offset,
+				    int *first_index, int *last_index,
+				    enum object_type type)
 {
-	int first = find_delta(base, type);
+	int first = find_ofs_delta(offset, type);
 	int last = first;
-	int end = nr_deltas - 1;
+	int end = nr_ofs_deltas - 1;
 
 	if (first < 0) {
 		*first_index = 0;
 		*last_index = -1;
 		return;
 	}
-	while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
+	while (first > 0 && ofs_deltas[first - 1].offset == offset)
 		--first;
-	while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
+	while (last < end && ofs_deltas[last + 1].offset == offset)
+		++last;
+	*first_index = first;
+	*last_index = last;
+}
+
+static int compare_ref_delta_bases(const unsigned char *sha1,
+				   const unsigned char *sha2,
+				   enum object_type type1,
+				   enum object_type type2)
+{
+	int cmp = type1 - type2;
+	if (cmp)
+		return cmp;
+	return hashcmp(sha1, sha2);
+}
+
+static int find_ref_delta(const unsigned char *sha1, enum object_type type)
+{
+	int first = 0, last = nr_ref_deltas;
+
+	while (first < last) {
+		int next = (first + last) / 2;
+		struct ref_delta_entry *delta = &ref_deltas[next];
+		int cmp;
+
+		cmp = compare_ref_delta_bases(sha1, delta->sha1,
+					      type, objects[delta->obj_no].type);
+		if (!cmp)
+			return next;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+	return -first-1;
+}
+
+static void find_ref_delta_children(const unsigned char *sha1,
+				    int *first_index, int *last_index,
+				    enum object_type type)
+{
+	int first = find_ref_delta(sha1, type);
+	int last = first;
+	int end = nr_ref_deltas - 1;
+
+	if (first < 0) {
+		*first_index = 0;
+		*last_index = -1;
+		return;
+	}
+	while (first > 0 && !hashcmp(ref_deltas[first - 1].sha1, sha1))
+		--first;
+	while (last < end && !hashcmp(ref_deltas[last + 1].sha1, sha1))
 		++last;
 	*first_index = first;
 	*last_index = last;
@@ -730,7 +786,7 @@
 	assert(data || obj_entry);
 
 	read_lock();
-	collision_test_needed = has_sha1_file(sha1);
+	collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
 	read_unlock();
 
 	if (collision_test_needed && !data) {
@@ -873,13 +929,15 @@
 	void *base_data, *delta_data;
 
 	if (show_stat) {
-		delta_obj->delta_depth = base->obj->delta_depth + 1;
+		int i = delta_obj - objects;
+		int j = base->obj - objects;
+		obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1;
 		deepest_delta_lock();
-		if (deepest_delta < delta_obj->delta_depth)
-			deepest_delta = delta_obj->delta_depth;
+		if (deepest_delta < obj_stat[i].delta_depth)
+			deepest_delta = obj_stat[i].delta_depth;
 		deepest_delta_unlock();
+		obj_stat[i].base_object_no = j;
 	}
-	delta_obj->base_object_no = base->obj - objects;
 	delta_data = get_data_from_pack(delta_obj);
 	base_data = get_base_data(base);
 	result->obj = delta_obj;
@@ -902,7 +960,7 @@
  * "want"; if so, swap in "set" and return true. Otherwise, leave it untouched
  * and return false.
  */
-static int compare_and_swap_type(enum object_type *type,
+static int compare_and_swap_type(signed char *type,
 				 enum object_type want,
 				 enum object_type set)
 {
@@ -921,16 +979,13 @@
 						  struct base_data *prev_base)
 {
 	if (base->ref_last == -1 && base->ofs_last == -1) {
-		union delta_base base_spec;
+		find_ref_delta_children(base->obj->idx.sha1,
+					&base->ref_first, &base->ref_last,
+					OBJ_REF_DELTA);
 
-		hashcpy(base_spec.sha1, base->obj->idx.sha1);
-		find_delta_children(&base_spec,
-				    &base->ref_first, &base->ref_last, OBJ_REF_DELTA);
-
-		memset(&base_spec, 0, sizeof(base_spec));
-		base_spec.offset = base->obj->idx.offset;
-		find_delta_children(&base_spec,
-				    &base->ofs_first, &base->ofs_last, OBJ_OFS_DELTA);
+		find_ofs_delta_children(base->obj->idx.offset,
+					&base->ofs_first, &base->ofs_last,
+					OBJ_OFS_DELTA);
 
 		if (base->ref_last == -1 && base->ofs_last == -1) {
 			free(base->data);
@@ -941,7 +996,7 @@
 	}
 
 	if (base->ref_first <= base->ref_last) {
-		struct object_entry *child = objects + deltas[base->ref_first].obj_no;
+		struct object_entry *child = objects + ref_deltas[base->ref_first].obj_no;
 		struct base_data *result = alloc_base_data();
 
 		if (!compare_and_swap_type(&child->real_type, OBJ_REF_DELTA,
@@ -957,7 +1012,7 @@
 	}
 
 	if (base->ofs_first <= base->ofs_last) {
-		struct object_entry *child = objects + deltas[base->ofs_first].obj_no;
+		struct object_entry *child = objects + ofs_deltas[base->ofs_first].obj_no;
 		struct base_data *result = alloc_base_data();
 
 		assert(child->real_type == OBJ_OFS_DELTA);
@@ -993,15 +1048,22 @@
 	}
 }
 
-static int compare_delta_entry(const void *a, const void *b)
+static int compare_ofs_delta_entry(const void *a, const void *b)
 {
-	const struct delta_entry *delta_a = a;
-	const struct delta_entry *delta_b = b;
+	const struct ofs_delta_entry *delta_a = a;
+	const struct ofs_delta_entry *delta_b = b;
 
-	/* group by type (ref vs ofs) and then by value (sha-1 or offset) */
-	return compare_delta_bases(&delta_a->base, &delta_b->base,
-				   objects[delta_a->obj_no].type,
-				   objects[delta_b->obj_no].type);
+	return delta_a->offset < delta_b->offset ? -1 :
+	       delta_a->offset > delta_b->offset ?  1 :
+	       0;
+}
+
+static int compare_ref_delta_entry(const void *a, const void *b)
+{
+	const struct ref_delta_entry *delta_a = a;
+	const struct ref_delta_entry *delta_b = b;
+
+	return hashcmp(delta_a->sha1, delta_b->sha1);
 }
 
 static void resolve_base(struct object_entry *obj)
@@ -1047,7 +1109,8 @@
 static void parse_pack_objects(unsigned char *sha1)
 {
 	int i, nr_delays = 0;
-	struct delta_entry *delta = deltas;
+	struct ofs_delta_entry *ofs_delta = ofs_deltas;
+	unsigned char ref_delta_sha1[20];
 	struct stat st;
 
 	if (verbose)
@@ -1056,12 +1119,18 @@
 				nr_objects);
 	for (i = 0; i < nr_objects; i++) {
 		struct object_entry *obj = &objects[i];
-		void *data = unpack_raw_entry(obj, &delta->base, obj->idx.sha1);
+		void *data = unpack_raw_entry(obj, &ofs_delta->offset,
+					      ref_delta_sha1, obj->idx.sha1);
 		obj->real_type = obj->type;
-		if (is_delta_type(obj->type)) {
-			nr_deltas++;
-			delta->obj_no = i;
-			delta++;
+		if (obj->type == OBJ_OFS_DELTA) {
+			nr_ofs_deltas++;
+			ofs_delta->obj_no = i;
+			ofs_delta++;
+		} else if (obj->type == OBJ_REF_DELTA) {
+			ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc);
+			hashcpy(ref_deltas[nr_ref_deltas].sha1, ref_delta_sha1);
+			ref_deltas[nr_ref_deltas].obj_no = i;
+			nr_ref_deltas++;
 		} else if (!data) {
 			/* large blobs, check later */
 			obj->real_type = OBJ_BAD;
@@ -1112,15 +1181,18 @@
 {
 	int i;
 
-	if (!nr_deltas)
+	if (!nr_ofs_deltas && !nr_ref_deltas)
 		return;
 
 	/* Sort deltas by base SHA1/offset for fast searching */
-	qsort(deltas, nr_deltas, sizeof(struct delta_entry),
-	      compare_delta_entry);
+	qsort(ofs_deltas, nr_ofs_deltas, sizeof(struct ofs_delta_entry),
+	      compare_ofs_delta_entry);
+	qsort(ref_deltas, nr_ref_deltas, sizeof(struct ref_delta_entry),
+	      compare_ref_delta_entry);
 
 	if (verbose)
-		progress = start_progress(_("Resolving deltas"), nr_deltas);
+		progress = start_progress(_("Resolving deltas"),
+					  nr_ref_deltas + nr_ofs_deltas);
 
 #ifndef NO_PTHREADS
 	nr_dispatched = 0;
@@ -1155,10 +1227,10 @@
  * - append objects to convert thin pack to full pack if required
  * - write the final 20-byte SHA-1
  */
-static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved);
+static void fix_unresolved_deltas(struct sha1file *f);
 static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
 {
-	if (nr_deltas == nr_resolved_deltas) {
+	if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) {
 		stop_progress(&progress);
 		/* Flush remaining pack final 20-byte SHA1. */
 		flush();
@@ -1169,7 +1241,7 @@
 		struct sha1file *f;
 		unsigned char read_sha1[20], tail_sha1[20];
 		struct strbuf msg = STRBUF_INIT;
-		int nr_unresolved = nr_deltas - nr_resolved_deltas;
+		int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas;
 		int nr_objects_initial = nr_objects;
 		if (nr_unresolved <= 0)
 			die(_("confusion beyond insanity"));
@@ -1177,7 +1249,7 @@
 		memset(objects + nr_objects + 1, 0,
 		       nr_unresolved * sizeof(*objects));
 		f = sha1fd(output_fd, curr_pack);
-		fix_unresolved_deltas(f, nr_unresolved);
+		fix_unresolved_deltas(f);
 		strbuf_addf(&msg, _("completed with %d local objects"),
 			    nr_objects - nr_objects_initial);
 		stop_progress_msg(&progress, msg.buf);
@@ -1191,11 +1263,11 @@
 			die(_("Unexpected tail checksum for %s "
 			      "(disk corruption?)"), curr_pack);
 	}
-	if (nr_deltas != nr_resolved_deltas)
+	if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas)
 		die(Q_("pack has %d unresolved delta",
 		       "pack has %d unresolved deltas",
-		       nr_deltas - nr_resolved_deltas),
-		    nr_deltas - nr_resolved_deltas);
+		       nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas),
+		    nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas);
 }
 
 static int write_compressed(struct sha1file *f, void *in, unsigned int size)
@@ -1254,15 +1326,15 @@
 
 static int delta_pos_compare(const void *_a, const void *_b)
 {
-	struct delta_entry *a = *(struct delta_entry **)_a;
-	struct delta_entry *b = *(struct delta_entry **)_b;
+	struct ref_delta_entry *a = *(struct ref_delta_entry **)_a;
+	struct ref_delta_entry *b = *(struct ref_delta_entry **)_b;
 	return a->obj_no - b->obj_no;
 }
 
-static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
+static void fix_unresolved_deltas(struct sha1file *f)
 {
-	struct delta_entry **sorted_by_pos;
-	int i, n = 0;
+	struct ref_delta_entry **sorted_by_pos;
+	int i;
 
 	/*
 	 * Since many unresolved deltas may well be themselves base objects
@@ -1274,29 +1346,26 @@
 	 * before deltas depending on them, a good heuristic is to start
 	 * resolving deltas in the same order as their position in the pack.
 	 */
-	sorted_by_pos = xmalloc(nr_unresolved * sizeof(*sorted_by_pos));
-	for (i = 0; i < nr_deltas; i++) {
-		if (objects[deltas[i].obj_no].real_type != OBJ_REF_DELTA)
-			continue;
-		sorted_by_pos[n++] = &deltas[i];
-	}
-	qsort(sorted_by_pos, n, sizeof(*sorted_by_pos), delta_pos_compare);
+	sorted_by_pos = xmalloc(nr_ref_deltas * sizeof(*sorted_by_pos));
+	for (i = 0; i < nr_ref_deltas; i++)
+		sorted_by_pos[i] = &ref_deltas[i];
+	qsort(sorted_by_pos, nr_ref_deltas, sizeof(*sorted_by_pos), delta_pos_compare);
 
-	for (i = 0; i < n; i++) {
-		struct delta_entry *d = sorted_by_pos[i];
+	for (i = 0; i < nr_ref_deltas; i++) {
+		struct ref_delta_entry *d = sorted_by_pos[i];
 		enum object_type type;
 		struct base_data *base_obj = alloc_base_data();
 
 		if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
 			continue;
-		base_obj->data = read_sha1_file(d->base.sha1, &type, &base_obj->size);
+		base_obj->data = read_sha1_file(d->sha1, &type, &base_obj->size);
 		if (!base_obj->data)
 			continue;
 
-		if (check_sha1_signature(d->base.sha1, base_obj->data,
+		if (check_sha1_signature(d->sha1, base_obj->data,
 				base_obj->size, typename(type)))
-			die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1));
-		base_obj->obj = append_obj_to_pack(f, d->base.sha1,
+			die(_("local object %s is corrupt"), sha1_to_hex(d->sha1));
+		base_obj->obj = append_obj_to_pack(f, d->sha1,
 					base_obj->data, base_obj->size, type);
 		find_unresolved_deltas(base_obj);
 		display_progress(progress, nr_resolved_deltas);
@@ -1488,7 +1557,7 @@
 
 static void show_pack_info(int stat_only)
 {
-	int i, baseobjects = nr_objects - nr_deltas;
+	int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas;
 	unsigned long *chain_histogram = NULL;
 
 	if (deepest_delta)
@@ -1498,7 +1567,7 @@
 		struct object_entry *obj = &objects[i];
 
 		if (is_delta_type(obj->type))
-			chain_histogram[obj->delta_depth - 1]++;
+			chain_histogram[obj_stat[i].delta_depth - 1]++;
 		if (stat_only)
 			continue;
 		printf("%s %-6s %lu %lu %"PRIuMAX,
@@ -1507,8 +1576,8 @@
 		       (unsigned long)(obj[1].idx.offset - obj->idx.offset),
 		       (uintmax_t)obj->idx.offset);
 		if (is_delta_type(obj->type)) {
-			struct object_entry *bobj = &objects[obj->base_object_no];
-			printf(" %u %s", obj->delta_depth, sha1_to_hex(bobj->idx.sha1));
+			struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
+			printf(" %u %s", obj_stat[i].delta_depth, sha1_to_hex(bobj->idx.sha1));
 		}
 		putchar('\n');
 	}
@@ -1671,11 +1740,14 @@
 	curr_pack = open_pack_file(pack_name);
 	parse_pack_header();
 	objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
-	deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
+	if (show_stat)
+		obj_stat = xcalloc(nr_objects + 1, sizeof(struct object_stat));
+	ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry));
 	parse_pack_objects(pack_sha1);
 	resolve_deltas();
 	conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
-	free(deltas);
+	free(ofs_deltas);
+	free(ref_deltas);
 	if (strict)
 		foreign_nr = check_objects();
 
diff --git a/builtin/init-db.c b/builtin/init-db.c
index ab9f86b..4335738 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -362,7 +362,6 @@
 static void separate_git_dir(const char *git_dir)
 {
 	struct stat st;
-	FILE *fp;
 
 	if (!stat(git_link, &st)) {
 		const char *src;
@@ -378,11 +377,7 @@
 			die_errno(_("unable to move %s to %s"), src, git_dir);
 	}
 
-	fp = fopen(git_link, "w");
-	if (!fp)
-		die(_("Could not create git link %s"), git_link);
-	fprintf(fp, "gitdir: %s\n", git_dir);
-	fclose(fp);
+	write_file(git_link, 1, "gitdir: %s\n", git_dir);
 }
 
 int init_db(const char *template_dir, unsigned int flags)
diff --git a/builtin/log.c b/builtin/log.c
index 4c4e6be..8781049 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -795,7 +795,7 @@
 static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 {
 	struct rev_info check_rev;
-	struct commit *commit;
+	struct commit *commit, *c1, *c2;
 	struct object *o1, *o2;
 	unsigned flags1, flags2;
 
@@ -803,9 +803,11 @@
 		die(_("Need exactly one range."));
 
 	o1 = rev->pending.objects[0].item;
-	flags1 = o1->flags;
 	o2 = rev->pending.objects[1].item;
+	flags1 = o1->flags;
 	flags2 = o2->flags;
+	c1 = lookup_commit_reference(o1->sha1);
+	c2 = lookup_commit_reference(o2->sha1);
 
 	if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
 		die(_("Not a range."));
@@ -827,10 +829,8 @@
 	}
 
 	/* reset for next revision walk */
-	clear_commit_marks((struct commit *)o1,
-			SEEN | UNINTERESTING | SHOWN | ADDED);
-	clear_commit_marks((struct commit *)o2,
-			SEEN | UNINTERESTING | SHOWN | ADDED);
+	clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED);
+	clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED);
 	o1->flags = flags1;
 	o2->flags = flags2;
 }
@@ -1632,16 +1632,13 @@
 		break;
 	default:
 		current_branch = branch_get(NULL);
-		if (!current_branch || !current_branch->merge
-					|| !current_branch->merge[0]
-					|| !current_branch->merge[0]->dst) {
+		upstream = branch_get_upstream(current_branch, NULL);
+		if (!upstream) {
 			fprintf(stderr, _("Could not find a tracked"
 					" remote branch, please"
 					" specify <upstream> manually.\n"));
 			usage_with_options(cherry_usage, options);
 		}
-
-		upstream = current_branch->merge[0]->dst;
 	}
 
 	init_revisions(&revs, prefix);
diff --git a/builtin/merge.c b/builtin/merge.c
index 3b0f8f9..85c54dc 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -492,8 +492,7 @@
 	}
 	if (len) {
 		struct strbuf truname = STRBUF_INIT;
-		strbuf_addstr(&truname, "refs/heads/");
-		strbuf_addstr(&truname, remote);
+		strbuf_addf(&truname, "refs/heads/%s", remote);
 		strbuf_setlen(&truname, truname.len - len);
 		if (ref_exists(truname.buf)) {
 			strbuf_addf(msg,
@@ -504,28 +503,7 @@
 			strbuf_release(&truname);
 			goto cleanup;
 		}
-	}
-
-	if (!strcmp(remote, "FETCH_HEAD") &&
-			!access(git_path("FETCH_HEAD"), R_OK)) {
-		const char *filename;
-		FILE *fp;
-		struct strbuf line = STRBUF_INIT;
-		char *ptr;
-
-		filename = git_path("FETCH_HEAD");
-		fp = fopen(filename, "r");
-		if (!fp)
-			die_errno(_("could not open '%s' for reading"),
-				  filename);
-		strbuf_getline(&line, fp, '\n');
-		fclose(fp);
-		ptr = strstr(line.buf, "\tnot-for-merge\t");
-		if (ptr)
-			strbuf_remove(&line, ptr-line.buf+1, 13);
-		strbuf_addbuf(msg, &line);
-		strbuf_release(&line);
-		goto cleanup;
+		strbuf_release(&truname);
 	}
 
 	if (remote_head->util) {
@@ -955,7 +933,7 @@
 
 	if (!branch)
 		die(_("No current branch."));
-	if (!branch->remote)
+	if (!branch->remote_name)
 		die(_("No remote for the current branch."));
 	if (!branch->merge_nr)
 		die(_("No default upstream defined for the current branch."));
@@ -1037,28 +1015,24 @@
 		st_stdin.st_mode == st_stdout.st_mode);
 }
 
-static struct commit_list *collect_parents(struct commit *head_commit,
-					   int *head_subsumed,
-					   int argc, const char **argv)
+static struct commit_list *reduce_parents(struct commit *head_commit,
+					  int *head_subsumed,
+					  struct commit_list *remoteheads)
 {
-	int i;
-	struct commit_list *remoteheads = NULL, *parents, *next;
-	struct commit_list **remotes = &remoteheads;
+	struct commit_list *parents, *next, **remotes = &remoteheads;
 
-	if (head_commit)
-		remotes = &commit_list_insert(head_commit, remotes)->next;
-	for (i = 0; i < argc; i++) {
-		struct commit *commit = get_merge_parent(argv[i]);
-		if (!commit)
-			help_unknown_ref(argv[i], "merge",
-					 "not something we can merge");
-		remotes = &commit_list_insert(commit, remotes)->next;
-	}
-	*remotes = NULL;
+	/*
+	 * Is the current HEAD reachable from another commit being
+	 * merged?  If so we do not want to record it as a parent of
+	 * the resulting merge, unless --no-ff is given.  We will flip
+	 * this variable to 0 when we find HEAD among the independent
+	 * tips being merged.
+	 */
+	*head_subsumed = 1;
 
+	/* Find what parents to record by checking independent ones. */
 	parents = reduce_heads(remoteheads);
 
-	*head_subsumed = 1; /* we will flip this to 0 when we find it */
 	for (remoteheads = NULL, remotes = &remoteheads;
 	     parents;
 	     parents = next) {
@@ -1068,10 +1042,122 @@
 			*head_subsumed = 0;
 		else
 			remotes = &commit_list_insert(commit, remotes)->next;
+		free(parents);
 	}
 	return remoteheads;
 }
 
+static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg)
+{
+	struct fmt_merge_msg_opts opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.add_title = !have_message;
+	opts.shortlog_len = shortlog_len;
+	opts.credit_people = (0 < option_edit);
+
+	fmt_merge_msg(merge_names, merge_msg, &opts);
+	if (merge_msg->len)
+		strbuf_setlen(merge_msg, merge_msg->len - 1);
+}
+
+static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names)
+{
+	const char *filename;
+	int fd, pos, npos;
+	struct strbuf fetch_head_file = STRBUF_INIT;
+
+	if (!merge_names)
+		merge_names = &fetch_head_file;
+
+	filename = git_path("FETCH_HEAD");
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		die_errno(_("could not open '%s' for reading"), filename);
+
+	if (strbuf_read(merge_names, fd, 0) < 0)
+		die_errno(_("could not read '%s'"), filename);
+	if (close(fd) < 0)
+		die_errno(_("could not close '%s'"), filename);
+
+	for (pos = 0; pos < merge_names->len; pos = npos) {
+		unsigned char sha1[20];
+		char *ptr;
+		struct commit *commit;
+
+		ptr = strchr(merge_names->buf + pos, '\n');
+		if (ptr)
+			npos = ptr - merge_names->buf + 1;
+		else
+			npos = merge_names->len;
+
+		if (npos - pos < 40 + 2 ||
+		    get_sha1_hex(merge_names->buf + pos, sha1))
+			commit = NULL; /* bad */
+		else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
+			continue; /* not-for-merge */
+		else {
+			char saved = merge_names->buf[pos + 40];
+			merge_names->buf[pos + 40] = '\0';
+			commit = get_merge_parent(merge_names->buf + pos);
+			merge_names->buf[pos + 40] = saved;
+		}
+		if (!commit) {
+			if (ptr)
+				*ptr = '\0';
+			die("not something we can merge in %s: %s",
+			    filename, merge_names->buf + pos);
+		}
+		remotes = &commit_list_insert(commit, remotes)->next;
+	}
+
+	if (merge_names == &fetch_head_file)
+		strbuf_release(&fetch_head_file);
+}
+
+static struct commit_list *collect_parents(struct commit *head_commit,
+					   int *head_subsumed,
+					   int argc, const char **argv,
+					   struct strbuf *merge_msg)
+{
+	int i;
+	struct commit_list *remoteheads = NULL;
+	struct commit_list **remotes = &remoteheads;
+	struct strbuf merge_names = STRBUF_INIT, *autogen = NULL;
+
+	if (merge_msg && (!have_message || shortlog_len))
+		autogen = &merge_names;
+
+	if (head_commit)
+		remotes = &commit_list_insert(head_commit, remotes)->next;
+
+	if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) {
+		handle_fetch_head(remotes, autogen);
+		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+	} else {
+		for (i = 0; i < argc; i++) {
+			struct commit *commit = get_merge_parent(argv[i]);
+			if (!commit)
+				help_unknown_ref(argv[i], "merge",
+						 "not something we can merge");
+			remotes = &commit_list_insert(commit, remotes)->next;
+		}
+		remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
+		if (autogen) {
+			struct commit_list *p;
+			for (p = remoteheads; p; p = p->next)
+				merge_name(merge_remote_util(p->item)->name, autogen);
+		}
+	}
+
+	if (autogen) {
+		prepare_merge_message(autogen, merge_msg);
+		strbuf_release(autogen);
+	}
+
+	return remoteheads;
+}
+
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
 	unsigned char result_tree[20];
@@ -1158,19 +1244,44 @@
 		option_commit = 0;
 	}
 
-	if (!abort_current_merge) {
-		if (!argc) {
-			if (default_to_upstream)
-				argc = setup_with_upstream(&argv);
-			else
-				die(_("No commit specified and merge.defaultToUpstream not set."));
-		} else if (argc == 1 && !strcmp(argv[0], "-"))
-			argv[0] = "@{-1}";
+	if (!argc) {
+		if (default_to_upstream)
+			argc = setup_with_upstream(&argv);
+		else
+			die(_("No commit specified and merge.defaultToUpstream not set."));
+	} else if (argc == 1 && !strcmp(argv[0], "-")) {
+		argv[0] = "@{-1}";
 	}
+
 	if (!argc)
 		usage_with_options(builtin_merge_usage,
 			builtin_merge_options);
 
+	if (!head_commit) {
+		struct commit *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (squash)
+			die(_("Squash commit into empty head not supported yet"));
+		if (fast_forward == FF_NO)
+			die(_("Non-fast-forward commit does not make sense into "
+			    "an empty head"));
+		remoteheads = collect_parents(head_commit, &head_subsumed,
+					      argc, argv, NULL);
+		remote_head = remoteheads->item;
+		if (!remote_head)
+			die(_("%s - not something we can merge"), argv[0]);
+		if (remoteheads->next)
+			die(_("Can merge only exactly one commit into empty head"));
+		read_empty(remote_head->object.sha1, 0);
+		update_ref("initial pull", "HEAD", remote_head->object.sha1,
+			   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+		goto done;
+	}
+
 	/*
 	 * This could be traditional "merge <msg> HEAD <commit>..."  and
 	 * the way we can tell it is to see if the second token is HEAD,
@@ -1179,40 +1290,16 @@
 	 * Traditional format never would have "-m" so it is an
 	 * additional safety measure to check for it.
 	 */
-
-	if (!have_message && head_commit &&
+	if (!have_message &&
 	    is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
+		warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
 		strbuf_addstr(&merge_msg, argv[0]);
 		head_arg = argv[1];
 		argv += 2;
 		argc -= 2;
-		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
-	} else if (!head_commit) {
-		struct commit *remote_head;
-		/*
-		 * If the merged head is a valid one there is no reason
-		 * to forbid "git merge" into a branch yet to be born.
-		 * We do the same for "git pull".
-		 */
-		if (argc != 1)
-			die(_("Can merge only exactly one commit into "
-				"empty head"));
-		if (squash)
-			die(_("Squash commit into empty head not supported yet"));
-		if (fast_forward == FF_NO)
-			die(_("Non-fast-forward commit does not make sense into "
-			    "an empty head"));
-		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
-		remote_head = remoteheads->item;
-		if (!remote_head)
-			die(_("%s - not something we can merge"), argv[0]);
-		read_empty(remote_head->object.sha1, 0);
-		update_ref("initial pull", "HEAD", remote_head->object.sha1,
-			   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
-		goto done;
+		remoteheads = collect_parents(head_commit, &head_subsumed,
+					      argc, argv, NULL);
 	} else {
-		struct strbuf merge_names = STRBUF_INIT;
-
 		/* We are invoked directly as the first-class UI. */
 		head_arg = "HEAD";
 
@@ -1221,21 +1308,8 @@
 		 * the standard merge summary message to be appended
 		 * to the given message.
 		 */
-		remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
-		for (p = remoteheads; p; p = p->next)
-			merge_name(merge_remote_util(p->item)->name, &merge_names);
-
-		if (!have_message || shortlog_len) {
-			struct fmt_merge_msg_opts opts;
-			memset(&opts, 0, sizeof(opts));
-			opts.add_title = !have_message;
-			opts.shortlog_len = shortlog_len;
-			opts.credit_people = (0 < option_edit);
-
-			fmt_merge_msg(&merge_names, &merge_msg, &opts);
-			if (merge_msg.len)
-				strbuf_setlen(&merge_msg, merge_msg.len - 1);
-		}
+		remoteheads = collect_parents(head_commit, &head_subsumed,
+					      argc, argv, &merge_msg);
 	}
 
 	if (!head_commit || !argc)
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 9736d44..248a3eb 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -138,9 +138,9 @@
 	return hashcmp(a->sha1, b->sha1);
 }
 
-static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
+static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
 {
-	struct object *o = parse_object(sha1);
+	struct object *o = parse_object(oid->hash);
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
@@ -160,7 +160,7 @@
 		}
 	}
 
-	add_to_tip_table(sha1, path, can_abbreviate_output);
+	add_to_tip_table(oid->hash, path, can_abbreviate_output);
 
 	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index c067107..80fe8c7 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -540,11 +540,11 @@
 	return WRITE_ONE_WRITTEN;
 }
 
-static int mark_tagged(const char *path, const unsigned char *sha1, int flag,
+static int mark_tagged(const char *path, const struct object_id *oid, int flag,
 		       void *cb_data)
 {
 	unsigned char peeled[20];
-	struct object_entry *entry = packlist_find(&to_pack, sha1, NULL);
+	struct object_entry *entry = packlist_find(&to_pack, oid->hash, NULL);
 
 	if (entry)
 		entry->tagged = 1;
@@ -2097,14 +2097,14 @@
 #define ll_find_deltas(l, s, w, d, p)	find_deltas(l, &s, w, d, p)
 #endif
 
-static int add_ref_tag(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
 {
-	unsigned char peeled[20];
+	struct object_id peeled;
 
 	if (starts_with(path, "refs/tags/") && /* is a tag? */
-	    !peel_ref(path, peeled)        && /* peelable? */
-	    packlist_find(&to_pack, peeled, NULL))      /* object packed? */
-		add_object_entry(sha1, OBJ_TAG, NULL, 0);
+	    !peel_ref(path, peeled.hash)    && /* peelable? */
+	    packlist_find(&to_pack, peeled.hash, NULL))      /* object packed? */
+		add_object_entry(oid->hash, OBJ_TAG, NULL, 0);
 	return 0;
 }
 
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 77db873..ba34dac 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,14 +1,14 @@
 #include "builtin.h"
 
-static void flush_current_id(int patchlen, unsigned char *id, unsigned char *result)
+static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
 {
 	char name[50];
 
 	if (!patchlen)
 		return;
 
-	memcpy(name, sha1_to_hex(id), 41);
-	printf("%s %s\n", sha1_to_hex(result), name);
+	memcpy(name, oid_to_hex(id), GIT_SHA1_HEXSZ + 1);
+	printf("%s %s\n", oid_to_hex(result), name);
 }
 
 static int remove_space(char *line)
@@ -53,23 +53,23 @@
 	return 1;
 }
 
-static void flush_one_hunk(unsigned char *result, git_SHA_CTX *ctx)
+static void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
 {
-	unsigned char hash[20];
+	unsigned char hash[GIT_SHA1_RAWSZ];
 	unsigned short carry = 0;
 	int i;
 
 	git_SHA1_Final(hash, ctx);
 	git_SHA1_Init(ctx);
 	/* 20-byte sum, with carry */
-	for (i = 0; i < 20; ++i) {
-		carry += result[i] + hash[i];
-		result[i] = carry;
+	for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
+		carry += result->hash[i] + hash[i];
+		result->hash[i] = carry;
 		carry >>= 8;
 	}
 }
 
-static int get_one_patchid(unsigned char *next_sha1, unsigned char *result,
+static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
 			   struct strbuf *line_buf, int stable)
 {
 	int patchlen = 0, found_next = 0;
@@ -77,7 +77,7 @@
 	git_SHA_CTX ctx;
 
 	git_SHA1_Init(&ctx);
-	hashclr(result);
+	oidclr(result);
 
 	while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
 		char *line = line_buf->buf;
@@ -93,7 +93,7 @@
 		else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
 			continue;
 
-		if (!get_sha1_hex(p, next_sha1)) {
+		if (!get_oid_hex(p, next_oid)) {
 			found_next = 1;
 			break;
 		}
@@ -143,7 +143,7 @@
 	}
 
 	if (!found_next)
-		hashclr(next_sha1);
+		oidclr(next_oid);
 
 	flush_one_hunk(result, &ctx);
 
@@ -152,15 +152,15 @@
 
 static void generate_id_list(int stable)
 {
-	unsigned char sha1[20], n[20], result[20];
+	struct object_id oid, n, result;
 	int patchlen;
 	struct strbuf line_buf = STRBUF_INIT;
 
-	hashclr(sha1);
+	oidclr(&oid);
 	while (!feof(stdin)) {
-		patchlen = get_one_patchid(n, result, &line_buf, stable);
-		flush_current_id(patchlen, sha1, result);
-		hashcpy(sha1, n);
+		patchlen = get_one_patchid(&n, &result, &line_buf, stable);
+		flush_current_id(patchlen, &oid, &result);
+		oidcpy(&oid, &n);
 	}
 	strbuf_release(&line_buf);
 }
diff --git a/builtin/prune.c b/builtin/prune.c
index 17094ad..0c73246 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -76,6 +76,95 @@
 	return 0;
 }
 
+static int prune_worktree(const char *id, struct strbuf *reason)
+{
+	struct stat st;
+	char *path;
+	int fd, len;
+
+	if (!is_directory(git_path("worktrees/%s", id))) {
+		strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
+		return 1;
+	}
+	if (file_exists(git_path("worktrees/%s/locked", id)))
+		return 0;
+	if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
+		strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
+		return 1;
+	}
+	fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
+	if (fd < 0) {
+		strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
+			    id, strerror(errno));
+		return 1;
+	}
+	len = st.st_size;
+	path = xmalloc(len + 1);
+	read_in_full(fd, path, len);
+	close(fd);
+	while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
+		len--;
+	if (!len) {
+		strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
+		free(path);
+		return 1;
+	}
+	path[len] = '\0';
+	if (!file_exists(path)) {
+		struct stat st_link;
+		free(path);
+		/*
+		 * the repo is moved manually and has not been
+		 * accessed since?
+		 */
+		if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
+		    st_link.st_nlink > 1)
+			return 0;
+		if (st.st_mtime <= expire) {
+			strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+	free(path);
+	return 0;
+}
+
+static void prune_worktrees(void)
+{
+	struct strbuf reason = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	DIR *dir = opendir(git_path("worktrees"));
+	struct dirent *d;
+	int ret;
+	if (!dir)
+		return;
+	while ((d = readdir(dir)) != NULL) {
+		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			continue;
+		strbuf_reset(&reason);
+		if (!prune_worktree(d->d_name, &reason))
+			continue;
+		if (show_only || verbose)
+			printf("%s\n", reason.buf);
+		if (show_only)
+			continue;
+		strbuf_reset(&path);
+		strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
+		ret = remove_dir_recursively(&path, 0);
+		if (ret < 0 && errno == ENOTDIR)
+			ret = unlink(path.buf);
+		if (ret)
+			error(_("failed to remove: %s"), strerror(errno));
+	}
+	closedir(dir);
+	if (!show_only)
+		rmdir(git_path("worktrees"));
+	strbuf_release(&reason);
+	strbuf_release(&path);
+}
+
 /*
  * Write errors (particularly out of space) can result in
  * failed temporary packs (and more rarely indexes and other
@@ -102,10 +191,12 @@
 {
 	struct rev_info revs;
 	struct progress *progress = NULL;
+	int do_prune_worktrees = 0;
 	const struct option options[] = {
 		OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
 		OPT__VERBOSE(&verbose, N_("report pruned objects")),
 		OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+		OPT_BOOL(0, "worktrees", &do_prune_worktrees, N_("prune .git/worktrees")),
 		OPT_EXPIRY_DATE(0, "expire", &expire,
 				N_("expire objects older than <time>")),
 		OPT_END()
@@ -119,6 +210,14 @@
 	init_revisions(&revs, prefix);
 
 	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+
+	if (do_prune_worktrees) {
+		if (argc)
+			die(_("--worktrees does not take extra arguments"));
+		prune_worktrees();
+		return 0;
+	}
+
 	while (argc--) {
 		unsigned char sha1[20];
 		const char *name = *argv++;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 5292bb5..94d0571 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -197,7 +197,7 @@
 	}
 }
 
-static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
+static int show_ref_cb(const char *path, const struct object_id *oid, int flag, void *unused)
 {
 	path = strip_namespace(path);
 	/*
@@ -210,7 +210,7 @@
 	 */
 	if (!path)
 		path = ".have";
-	show_ref(path, sha1);
+	show_ref(path, oid->hash);
 	return 0;
 }
 
@@ -228,6 +228,7 @@
 static void write_head_info(void)
 {
 	struct sha1_array sa = SHA1_ARRAY_INIT;
+
 	for_each_alternate_ref(collect_one_alternate_ref, &sa);
 	sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
 	sha1_array_clear(&sa);
@@ -1008,7 +1009,7 @@
 	int argc;
 	const char **argv;
 	struct child_process proc = CHILD_PROCESS_INIT;
-	char *hook;
+	const char *hook;
 
 	hook = find_hook("post-update");
 	for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 8182b64..c2eb8ff 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -313,14 +313,14 @@
 	return 0;
 }
 
-static int push_tip_to_list(const char *refname, const unsigned char *sha1,
+static int push_tip_to_list(const char *refname, const struct object_id *oid,
 			    int flags, void *cb_data)
 {
 	struct commit_list **list = cb_data;
 	struct commit *tip_commit;
 	if (flags & REF_ISSYMREF)
 		return 0;
-	tip_commit = lookup_commit_reference_gently(sha1, 1);
+	tip_commit = lookup_commit_reference_gently(oid->hash, 1);
 	if (!tip_commit)
 		return 0;
 	commit_list_insert(tip_commit, list);
@@ -352,6 +352,7 @@
 	if (cb->unreachable_expire_kind != UE_ALWAYS) {
 		if (cb->unreachable_expire_kind == UE_HEAD) {
 			struct commit_list *elem;
+
 			for_each_ref(push_tip_to_list, &cb->tips);
 			for (elem = cb->tips; elem; elem = elem->next)
 				commit_list_insert(elem->item, &cb->mark_list);
@@ -379,14 +380,14 @@
 	}
 }
 
-static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
 {
 	struct collected_reflog *e;
 	struct collect_reflog_cb *cb = cb_data;
 	size_t namelen = strlen(ref);
 
 	e = xmalloc(sizeof(*e) + namelen + 1);
-	hashcpy(e->sha1, sha1);
+	hashcpy(e->sha1, oid->hash);
 	memcpy(e->reflog, ref, namelen + 1);
 	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
 	cb->e[cb->nr++] = e;
diff --git a/builtin/remote.c b/builtin/remote.c
index 5d3ab90..f4a6ec9 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -509,11 +509,10 @@
 };
 
 static int add_branch_for_removal(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
+	const struct object_id *oid, int flags, void *cb_data)
 {
 	struct branches_for_remote *branches = cb_data;
 	struct refspec refspec;
-	struct string_list_item *item;
 	struct known_remote *kr;
 
 	memset(&refspec, 0, sizeof(refspec));
@@ -543,9 +542,7 @@
 	if (flags & REF_ISSYMREF)
 		return unlink(git_path("%s", refname));
 
-	item = string_list_append(branches->branches, refname);
-	item->util = xmalloc(20);
-	hashcpy(item->util, sha1);
+	string_list_append(branches->branches, refname);
 
 	return 0;
 }
@@ -557,20 +554,20 @@
 };
 
 static int read_remote_branches(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
+	const struct object_id *oid, int flags, void *cb_data)
 {
 	struct rename_info *rename = cb_data;
 	struct strbuf buf = STRBUF_INIT;
 	struct string_list_item *item;
 	int flag;
-	unsigned char orig_sha1[20];
+	struct object_id orig_oid;
 	const char *symref;
 
 	strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
 	if (starts_with(refname, buf.buf)) {
 		item = string_list_append(rename->remote_branches, xstrdup(refname));
 		symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
-					    orig_sha1, &flag);
+					    orig_oid.hash, &flag);
 		if (flag & REF_ISSYMREF)
 			item->util = xstrdup(symref);
 		else
@@ -584,7 +581,7 @@
 {
 	struct strbuf buf = STRBUF_INIT;
 	int i;
-	char *path = NULL;
+	const char *path = NULL;
 
 	strbuf_addf(&buf, "remote.%s.url", remote->name);
 	for (i = 0; i < remote->url_nr; i++)
@@ -704,9 +701,9 @@
 	for (i = 0; i < remote_branches.nr; i++) {
 		struct string_list_item *item = remote_branches.items + i;
 		int flag = 0;
-		unsigned char sha1[20];
+		struct object_id oid;
 
-		read_ref_full(item->string, RESOLVE_REF_READING, sha1, &flag);
+		read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
 		if (!(flag & REF_ISSYMREF))
 			continue;
 		if (delete_ref(item->string, NULL, REF_NODEREF))
@@ -826,7 +823,7 @@
 
 	if (!result)
 		result = remove_branches(&branches);
-	string_list_clear(&branches, 1);
+	string_list_clear(&branches, 0);
 
 	if (skipped.nr) {
 		fprintf_ln(stderr,
@@ -867,7 +864,7 @@
 }
 
 static int append_ref_to_tracked_list(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
+	const struct object_id *oid, int flags, void *cb_data)
 {
 	struct ref_states *states = cb_data;
 	struct refspec refspec;
diff --git a/builtin/repack.c b/builtin/repack.c
index f2edeb0..af7340c 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -285,7 +285,8 @@
 	failed = 0;
 	for_each_string_list_item(item, &names) {
 		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
-			char *fname, *fname_old;
+			const char *fname_old;
+			char *fname;
 			fname = mkpathdup("%s/pack-%s%s", packdir,
 						item->string, exts[ext].name);
 			if (!file_exists(fname)) {
@@ -313,7 +314,8 @@
 	if (failed) {
 		struct string_list rollback_failure = STRING_LIST_INIT_DUP;
 		for_each_string_list_item(item, &rollback) {
-			char *fname, *fname_old;
+			const char *fname_old;
+			char *fname;
 			fname = mkpathdup("%s/%s", packdir, item->string);
 			fname_old = mkpath("%s/old-%s", packdir, item->string);
 			if (rename(fname_old, fname))
@@ -366,7 +368,7 @@
 	/* Remove the "old-" files */
 	for_each_string_list_item(item, &names) {
 		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
-			char *fname;
+			const char *fname;
 			fname = mkpath("%s/old-%s%s",
 					packdir,
 					item->string,
diff --git a/builtin/replace.c b/builtin/replace.c
index 54bf01a..0d52e7f 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -35,7 +35,7 @@
 	enum replace_format format;
 };
 
-static int show_reference(const char *refname, const unsigned char *sha1,
+static int show_reference(const char *refname, const struct object_id *oid,
 			  int flag, void *cb_data)
 {
 	struct show_data *data = cb_data;
@@ -44,19 +44,19 @@
 		if (data->format == REPLACE_FORMAT_SHORT)
 			printf("%s\n", refname);
 		else if (data->format == REPLACE_FORMAT_MEDIUM)
-			printf("%s -> %s\n", refname, sha1_to_hex(sha1));
+			printf("%s -> %s\n", refname, oid_to_hex(oid));
 		else { /* data->format == REPLACE_FORMAT_LONG */
-			unsigned char object[20];
+			struct object_id object;
 			enum object_type obj_type, repl_type;
 
-			if (get_sha1(refname, object))
+			if (get_sha1(refname, object.hash))
 				return error("Failed to resolve '%s' as a valid ref.", refname);
 
-			obj_type = sha1_object_info(object, NULL);
-			repl_type = sha1_object_info(sha1, NULL);
+			obj_type = sha1_object_info(object.hash, NULL);
+			repl_type = sha1_object_info(oid->hash, NULL);
 
 			printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
-			       sha1_to_hex(sha1), typename(repl_type));
+			       oid_to_hex(oid), typename(repl_type));
 		}
 	}
 
@@ -82,7 +82,7 @@
 		    "valid formats are 'short', 'medium' and 'long'\n",
 		    format);
 
-	for_each_replace_ref(show_reference, (void *) &data);
+	for_each_replace_ref(show_reference, (void *)&data);
 
 	return 0;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index ff84a82..c0b4b53 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -42,6 +42,7 @@
 "    --abbrev=<n> | --no-abbrev\n"
 "    --abbrev-commit\n"
 "    --left-right\n"
+"    --count\n"
 "  special purpose:\n"
 "    --bisect\n"
 "    --bisect-vars\n"
@@ -355,7 +356,7 @@
 	if (bisect_list)
 		revs.limited = 1;
 
-	if (use_bitmap_index) {
+	if (use_bitmap_index && !revs.prune) {
 		if (revs.count && !revs.left_right && !revs.cherry_mark) {
 			uint32_t commit_count;
 			if (!prepare_bitmap_walk(&revs)) {
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 3626c61..b623239 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -190,17 +190,17 @@
 	return 0;
 }
 
-static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
 {
 	if (ref_excluded(ref_excludes, refname))
 		return 0;
-	show_rev(NORMAL, sha1, refname);
+	show_rev(NORMAL, oid->hash, refname);
 	return 0;
 }
 
-static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
 {
-	show_rev(REVERSED, sha1, refname);
+	show_rev(REVERSED, oid->hash, refname);
 	return 0;
 }
 
@@ -533,6 +533,13 @@
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
+		if (!strcmp(arg, "--git-path")) {
+			if (!argv[i + 1])
+				die("--git-path requires an argument");
+			puts(git_path("%s", argv[i + 1]));
+			i++;
+			continue;
+		}
 		if (as_is) {
 			if (show_file(arg, output_prefix) && as_is < 2)
 				verify_filename(prefix, arg, 0);
@@ -755,6 +762,10 @@
 				free(cwd);
 				continue;
 			}
+			if (!strcmp(arg, "--git-common-dir")) {
+				puts(get_git_common_dir());
+				continue;
+			}
 			if (!strcmp(arg, "--resolve-git-dir")) {
 				const char *gitdir = argv[++i];
 				if (!gitdir)
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index f3fb5fb..323f857 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -369,10 +369,10 @@
 	      compare_ref_name);
 }
 
-static int append_ref(const char *refname, const unsigned char *sha1,
+static int append_ref(const char *refname, const struct object_id *oid,
 		      int allow_dups)
 {
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
 	int i;
 
 	if (!commit)
@@ -394,39 +394,42 @@
 	return 0;
 }
 
-static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_head_ref(const char *refname, const struct object_id *oid,
+			   int flag, void *cb_data)
 {
-	unsigned char tmp[20];
+	struct object_id tmp;
 	int ofs = 11;
 	if (!starts_with(refname, "refs/heads/"))
 		return 0;
 	/* If both heads/foo and tags/foo exists, get_sha1 would
 	 * get confused.
 	 */
-	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+	if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
 		ofs = 5;
-	return append_ref(refname + ofs, sha1, 0);
+	return append_ref(refname + ofs, oid, 0);
 }
 
-static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_remote_ref(const char *refname, const struct object_id *oid,
+			     int flag, void *cb_data)
 {
-	unsigned char tmp[20];
+	struct object_id tmp;
 	int ofs = 13;
 	if (!starts_with(refname, "refs/remotes/"))
 		return 0;
 	/* If both heads/foo and tags/foo exists, get_sha1 would
 	 * get confused.
 	 */
-	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+	if (get_sha1(refname + ofs, tmp.hash) || oidcmp(&tmp, oid))
 		ofs = 5;
-	return append_ref(refname + ofs, sha1, 0);
+	return append_ref(refname + ofs, oid, 0);
 }
 
-static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_tag_ref(const char *refname, const struct object_id *oid,
+			  int flag, void *cb_data)
 {
 	if (!starts_with(refname, "refs/tags/"))
 		return 0;
-	return append_ref(refname + 5, sha1, 0);
+	return append_ref(refname + 5, oid, 0);
 }
 
 static const char *match_ref_pattern = NULL;
@@ -440,7 +443,8 @@
 	return cnt;
 }
 
-static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int append_matching_ref(const char *refname, const struct object_id *oid,
+			       int flag, void *cb_data)
 {
 	/* we want to allow pattern hold/<asterisk> to show all
 	 * branches under refs/heads/hold/, and v0.99.9? to show
@@ -456,21 +460,23 @@
 	if (wildmatch(match_ref_pattern, tail, 0, NULL))
 		return 0;
 	if (starts_with(refname, "refs/heads/"))
-		return append_head_ref(refname, sha1, flag, cb_data);
+		return append_head_ref(refname, oid, flag, cb_data);
 	if (starts_with(refname, "refs/tags/"))
-		return append_tag_ref(refname, sha1, flag, cb_data);
-	return append_ref(refname, sha1, 0);
+		return append_tag_ref(refname, oid, flag, cb_data);
+	return append_ref(refname, oid, 0);
 }
 
 static void snarf_refs(int head, int remotes)
 {
 	if (head) {
 		int orig_cnt = ref_name_cnt;
+
 		for_each_ref(append_head_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 	if (remotes) {
 		int orig_cnt = ref_name_cnt;
+
 		for_each_ref(append_remote_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
@@ -530,14 +536,15 @@
 
 static void append_one_rev(const char *av)
 {
-	unsigned char revkey[20];
-	if (!get_sha1(av, revkey)) {
-		append_ref(av, revkey, 0);
+	struct object_id revkey;
+	if (!get_sha1(av, revkey.hash)) {
+		append_ref(av, &revkey, 0);
 		return;
 	}
 	if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
 		/* glob style match */
 		int saved_matches = ref_name_cnt;
+
 		match_ref_pattern = av;
 		match_ref_slash = count_slash(av);
 		for_each_ref(append_matching_ref, NULL);
@@ -636,7 +643,7 @@
 	char head[128];
 	const char *head_p;
 	int head_len;
-	unsigned char head_sha1[20];
+	struct object_id head_oid;
 	int merge_base = 0;
 	int independent = 0;
 	int no_name = 0;
@@ -718,11 +725,11 @@
 	}
 
 	/* If nothing is specified, show all branches by default */
-	if (ac + all_heads + all_remotes == 0)
+	if (ac <= topics && all_heads + all_remotes == 0)
 		all_heads = 1;
 
 	if (reflog) {
-		unsigned char sha1[20];
+		struct object_id oid;
 		char nth_desc[256];
 		char *ref;
 		int base = 0;
@@ -733,7 +740,7 @@
 
 			fake_av[0] = resolve_refdup("HEAD",
 						    RESOLVE_REF_READING,
-						    sha1, NULL);
+						    oid.hash, NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
@@ -744,7 +751,7 @@
 		if (MAX_REVS < reflog)
 			die("Only %d entries can be shown at one time.",
 			    MAX_REVS);
-		if (!dwim_ref(*av, strlen(*av), sha1, &ref))
+		if (!dwim_ref(*av, strlen(*av), oid.hash, &ref))
 			die("No such ref %s", *av);
 
 		/* Has the base been specified? */
@@ -755,7 +762,7 @@
 				/* Ah, that is a date spec... */
 				unsigned long at;
 				at = approxidate(reflog_base);
-				read_ref_at(ref, flags, at, -1, sha1, NULL,
+				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
 			}
 		}
@@ -766,7 +773,7 @@
 			unsigned long timestamp;
 			int tz;
 
-			if (read_ref_at(ref, flags, 0, base+i, sha1, &logmsg,
+			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
 					&timestamp, &tz, NULL)) {
 				reflog = i;
 				break;
@@ -781,21 +788,21 @@
 						msg);
 			free(logmsg);
 			sprintf(nth_desc, "%s@{%d}", *av, base+i);
-			append_ref(nth_desc, sha1, 1);
+			append_ref(nth_desc, &oid, 1);
 		}
 		free(ref);
 	}
-	else if (all_heads + all_remotes)
-		snarf_refs(all_heads, all_remotes);
 	else {
 		while (0 < ac) {
 			append_one_rev(*av);
 			ac--; av++;
 		}
+		if (all_heads + all_remotes)
+			snarf_refs(all_heads, all_remotes);
 	}
 
 	head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-				    head_sha1, NULL);
+				    head_oid.hash, NULL);
 	if (head_p) {
 		head_len = strlen(head_p);
 		memcpy(head, head_p, head_len + 1);
@@ -814,7 +821,7 @@
 			if (rev_is_head(head,
 					head_len,
 					ref_name[i],
-					head_sha1, NULL))
+					head_oid.hash, NULL))
 				has_head++;
 		}
 		if (!has_head) {
@@ -829,17 +836,17 @@
 	}
 
 	for (num_rev = 0; ref_name[num_rev]; num_rev++) {
-		unsigned char revkey[20];
+		struct object_id revkey;
 		unsigned int flag = 1u << (num_rev + REV_SHIFT);
 
 		if (MAX_REVS <= num_rev)
 			die("cannot handle more than %d revs.", MAX_REVS);
-		if (get_sha1(ref_name[num_rev], revkey))
+		if (get_sha1(ref_name[num_rev], revkey.hash))
 			die("'%s' is not a valid ref.", ref_name[num_rev]);
-		commit = lookup_commit_reference(revkey);
+		commit = lookup_commit_reference(revkey.hash);
 		if (!commit)
 			die("cannot find commit %s (%s)",
-			    ref_name[num_rev], revkey);
+			    ref_name[num_rev], oid_to_hex(&revkey));
 		parse_commit(commit);
 		mark_seen(commit, &seen);
 
@@ -873,7 +880,7 @@
 			int is_head = rev_is_head(head,
 						  head_len,
 						  ref_name[i],
-						  head_sha1,
+						  head_oid.hash,
 						  rev[i]->object.sha1);
 			if (extra < 0)
 				printf("%c [%s] ",
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index afb1030..dfbc314 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -17,19 +17,20 @@
 static const char **pattern;
 static const char *exclude_existing_arg;
 
-static void show_one(const char *refname, const unsigned char *sha1)
+static void show_one(const char *refname, const struct object_id *oid)
 {
-	const char *hex = find_unique_abbrev(sha1, abbrev);
+	const char *hex = find_unique_abbrev(oid->hash, abbrev);
 	if (hash_only)
 		printf("%s\n", hex);
 	else
 		printf("%s %s\n", hex, refname);
 }
 
-static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+static int show_ref(const char *refname, const struct object_id *oid,
+		    int flag, void *cbdata)
 {
 	const char *hex;
-	unsigned char peeled[20];
+	struct object_id peeled;
 
 	if (show_head && !strcmp(refname, "HEAD"))
 		goto match;
@@ -69,26 +70,27 @@
 	 * detect and return error if the repository is corrupt and
 	 * ref points at a nonexistent object.
 	 */
-	if (!has_sha1_file(sha1))
+	if (!has_sha1_file(oid->hash))
 		die("git show-ref: bad ref %s (%s)", refname,
-		    sha1_to_hex(sha1));
+		    oid_to_hex(oid));
 
 	if (quiet)
 		return 0;
 
-	show_one(refname, sha1);
+	show_one(refname, oid);
 
 	if (!deref_tags)
 		return 0;
 
-	if (!peel_ref(refname, peeled)) {
-		hex = find_unique_abbrev(peeled, abbrev);
+	if (!peel_ref(refname, peeled.hash)) {
+		hex = find_unique_abbrev(peeled.hash, abbrev);
 		printf("%s %s^{}\n", hex, refname);
 	}
 	return 0;
 }
 
-static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+static int add_existing(const char *refname, const struct object_id *oid,
+			int flag, void *cbdata)
 {
 	struct string_list *list = (struct string_list *)cbdata;
 	string_list_insert(list, refname);
@@ -208,12 +210,12 @@
 		if (!pattern)
 			die("--verify requires a reference");
 		while (*pattern) {
-			unsigned char sha1[20];
+			struct object_id oid;
 
 			if (starts_with(*pattern, "refs/") &&
-			    !read_ref(*pattern, sha1)) {
+			    !read_ref(*pattern, oid.hash)) {
 				if (!quiet)
-					show_one(*pattern, sha1);
+					show_one(*pattern, &oid);
 			}
 			else if (!quiet)
 				die("'%s' - not a valid ref", *pattern);
diff --git a/builtin/tag.c b/builtin/tag.c
index 6f07ac6..5f6cdc5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -176,7 +176,7 @@
 	return contains_test(candidate, want);
 }
 
-static void show_tag_lines(const unsigned char *sha1, int lines)
+static void show_tag_lines(const struct object_id *oid, int lines)
 {
 	int i;
 	unsigned long size;
@@ -184,14 +184,14 @@
 	char *buf, *sp, *eol;
 	size_t len;
 
-	buf = read_sha1_file(sha1, &type, &size);
+	buf = read_sha1_file(oid->hash, &type, &size);
 	if (!buf)
-		die_errno("unable to read object %s", sha1_to_hex(sha1));
+		die_errno("unable to read object %s", oid_to_hex(oid));
 	if (type != OBJ_COMMIT && type != OBJ_TAG)
 		goto free_return;
 	if (!size)
 		die("an empty %s object %s?",
-		    typename(type), sha1_to_hex(sha1));
+		    typename(type), oid_to_hex(oid));
 
 	/* skip header */
 	sp = strstr(buf, "\n\n");
@@ -215,7 +215,7 @@
 	free(buf);
 }
 
-static int show_reference(const char *refname, const unsigned char *sha1,
+static int show_reference(const char *refname, const struct object_id *oid,
 			  int flag, void *cb_data)
 {
 	struct tag_filter *filter = cb_data;
@@ -224,14 +224,14 @@
 		if (filter->with_commit) {
 			struct commit *commit;
 
-			commit = lookup_commit_reference_gently(sha1, 1);
+			commit = lookup_commit_reference_gently(oid->hash, 1);
 			if (!commit)
 				return 0;
 			if (!contains(commit, filter->with_commit))
 				return 0;
 		}
 
-		if (points_at.nr && !match_points_at(refname, sha1))
+		if (points_at.nr && !match_points_at(refname, oid->hash))
 			return 0;
 
 		if (!filter->lines) {
@@ -242,7 +242,7 @@
 			return 0;
 		}
 		printf("%-15s ", refname);
-		show_tag_lines(sha1, filter->lines);
+		show_tag_lines(oid, filter->lines);
 		putchar('\n');
 	}
 
@@ -268,7 +268,7 @@
 	memset(&filter.tags, 0, sizeof(filter.tags));
 	filter.tags.strdup_strings = 1;
 
-	for_each_tag_ref(show_reference, (void *) &filter);
+	for_each_tag_ref(show_reference, (void *)&filter);
 	if (sort) {
 		int i;
 		if ((sort & SORT_MASK) == VERCMP_SORT)
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 6271b54..7431938 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -33,6 +33,7 @@
 static int mark_skip_worktree_only;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
+static struct strbuf mtime_dir = STRBUF_INIT;
 
 __attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
@@ -48,6 +49,166 @@
 	va_end(vp);
 }
 
+static void remove_test_directory(void)
+{
+	if (mtime_dir.len)
+		remove_dir_recursively(&mtime_dir, 0);
+}
+
+static const char *get_mtime_path(const char *path)
+{
+	static struct strbuf sb = STRBUF_INIT;
+	strbuf_reset(&sb);
+	strbuf_addf(&sb, "%s/%s", mtime_dir.buf, path);
+	return sb.buf;
+}
+
+static void xmkdir(const char *path)
+{
+	path = get_mtime_path(path);
+	if (mkdir(path, 0700))
+		die_errno(_("failed to create directory %s"), path);
+}
+
+static int xstat_mtime_dir(struct stat *st)
+{
+	if (stat(mtime_dir.buf, st))
+		die_errno(_("failed to stat %s"), mtime_dir.buf);
+	return 0;
+}
+
+static int create_file(const char *path)
+{
+	int fd;
+	path = get_mtime_path(path);
+	fd = open(path, O_CREAT | O_RDWR, 0644);
+	if (fd < 0)
+		die_errno(_("failed to create file %s"), path);
+	return fd;
+}
+
+static void xunlink(const char *path)
+{
+	path = get_mtime_path(path);
+	if (unlink(path))
+		die_errno(_("failed to delete file %s"), path);
+}
+
+static void xrmdir(const char *path)
+{
+	path = get_mtime_path(path);
+	if (rmdir(path))
+		die_errno(_("failed to delete directory %s"), path);
+}
+
+static void avoid_racy(void)
+{
+	/*
+	 * not use if we could usleep(10) if USE_NSEC is defined. The
+	 * field nsec could be there, but the OS could choose to
+	 * ignore it?
+	 */
+	sleep(1);
+}
+
+static int test_if_untracked_cache_is_supported(void)
+{
+	struct stat st;
+	struct stat_data base;
+	int fd, ret = 0;
+
+	strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX");
+	if (!mkdtemp(mtime_dir.buf))
+		die_errno("Could not make temporary directory");
+
+	fprintf(stderr, _("Testing "));
+	atexit(remove_test_directory);
+	xstat_mtime_dir(&st);
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	fd = create_file("newfile");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		close(fd);
+		fputc('\n', stderr);
+		fprintf_ln(stderr,_("directory stat info does not "
+				    "change after adding a new file"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	xmkdir("new-dir");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		close(fd);
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not change "
+				     "after adding a new directory"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	write_or_die(fd, "data", 4);
+	close(fd);
+	xstat_mtime_dir(&st);
+	if (match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info changes "
+				     "after updating a file"));
+		goto done;
+	}
+	fputc('.', stderr);
+
+	avoid_racy();
+	close(create_file("new-dir/new"));
+	xstat_mtime_dir(&st);
+	if (match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info changes after "
+				     "adding a file inside subdirectory"));
+		goto done;
+	}
+	fputc('.', stderr);
+
+	avoid_racy();
+	xunlink("newfile");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not "
+				     "change after deleting a file"));
+		goto done;
+	}
+	fill_stat_data(&base, &st);
+	fputc('.', stderr);
+
+	avoid_racy();
+	xunlink("new-dir/new");
+	xrmdir("new-dir");
+	xstat_mtime_dir(&st);
+	if (!match_stat_data(&base, &st)) {
+		fputc('\n', stderr);
+		fprintf_ln(stderr, _("directory stat info does not "
+				     "change after deleting a directory"));
+		goto done;
+	}
+
+	if (rmdir(mtime_dir.buf))
+		die_errno(_("failed to delete directory %s"), mtime_dir.buf);
+	fprintf_ln(stderr, _(" OK"));
+	ret = 1;
+
+done:
+	strbuf_release(&mtime_dir);
+	return ret;
+}
+
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
 	int namelen = strlen(path);
@@ -532,10 +693,9 @@
 
 	for (i = 1; i < ac; i++) {
 		const char *arg = av[i];
-		const char *p = prefix_path(prefix, prefix_length, arg);
+		char *p = prefix_path(prefix, prefix_length, arg);
 		err |= unresolve_one(p);
-		if (p < arg || p > arg + strlen(arg))
-			free((char *)p);
+		free(p);
 	}
 	return err;
 }
@@ -742,6 +902,7 @@
 int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
 	int newfd, entries, has_errors = 0, line_termination = '\n';
+	int untracked_cache = -1;
 	int read_from_stdin = 0;
 	int prefix_length = prefix ? strlen(prefix) : 0;
 	int preferred_index_format = 0;
@@ -833,6 +994,10 @@
 			N_("write index in this format")),
 		OPT_BOOL(0, "split-index", &split_index,
 			N_("enable or disable split index")),
+		OPT_BOOL(0, "untracked-cache", &untracked_cache,
+			N_("enable/disable untracked cache")),
+		OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
+			    N_("enable untracked cache without testing the filesystem"), 2),
 		OPT_END()
 	};
 
@@ -871,14 +1036,14 @@
 		case PARSE_OPT_DONE:
 		{
 			const char *path = ctx.argv[0];
-			const char *p;
+			char *p;
 
 			setup_work_tree();
 			p = prefix_path(prefix, prefix_length, path);
 			update_one(p);
 			if (set_executable_bit)
 				chmod_path(set_executable_bit, p);
-			free((char *)p);
+			free(p);
 			ctx.argc--;
 			ctx.argv++;
 			break;
@@ -909,7 +1074,7 @@
 
 		setup_work_tree();
 		while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
-			const char *p;
+			char *p;
 			if (line_termination && buf.buf[0] == '"') {
 				strbuf_reset(&nbuf);
 				if (unquote_c_style(&nbuf, buf.buf, NULL))
@@ -920,7 +1085,7 @@
 			update_one(p);
 			if (set_executable_bit)
 				chmod_path(set_executable_bit, p);
-			free((char *)p);
+			free(p);
 		}
 		strbuf_release(&nbuf);
 		strbuf_release(&buf);
@@ -939,6 +1104,28 @@
 		the_index.split_index = NULL;
 		the_index.cache_changed |= SOMETHING_CHANGED;
 	}
+	if (untracked_cache > 0) {
+		struct untracked_cache *uc;
+
+		if (untracked_cache < 2) {
+			setup_work_tree();
+			if (!test_if_untracked_cache_is_supported())
+				return 1;
+		}
+		if (!the_index.untracked) {
+			uc = xcalloc(1, sizeof(*uc));
+			strbuf_init(&uc->ident, 100);
+			uc->exclude_per_dir = ".gitignore";
+			/* should be the same flags used by git-status */
+			uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+			the_index.untracked = uc;
+		}
+		add_untracked_ident(the_index.untracked);
+		the_index.cache_changed |= UNTRACKED_CHANGED;
+	} else if (!untracked_cache && the_index.untracked) {
+		the_index.untracked = NULL;
+		the_index.cache_changed |= UNTRACKED_CHANGED;
+	}
 
 	if (active_cache_changed) {
 		if (newfd < 0) {
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 8d157eb..7cffc3a 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -24,7 +24,7 @@
 
 static void finish_bulk_checkin(struct bulk_checkin_state *state)
 {
-	unsigned char sha1[20];
+	struct object_id oid;
 	struct strbuf packname = STRBUF_INIT;
 	int i;
 
@@ -36,11 +36,11 @@
 		unlink(state->pack_tmp_name);
 		goto clear_exit;
 	} else if (state->nr_written == 1) {
-		sha1close(state->f, sha1, CSUM_FSYNC);
+		sha1close(state->f, oid.hash, CSUM_FSYNC);
 	} else {
-		int fd = sha1close(state->f, sha1, 0);
-		fixup_pack_header_footer(fd, sha1, state->pack_tmp_name,
-					 state->nr_written, sha1,
+		int fd = sha1close(state->f, oid.hash, 0);
+		fixup_pack_header_footer(fd, oid.hash, state->pack_tmp_name,
+					 state->nr_written, oid.hash,
 					 state->offset);
 		close(fd);
 	}
@@ -48,7 +48,7 @@
 	strbuf_addf(&packname, "%s/pack/pack-", get_object_directory());
 	finish_tmp_packfile(&packname, state->pack_tmp_name,
 			    state->written, state->nr_written,
-			    &state->pack_idx_opts, sha1);
+			    &state->pack_idx_opts, oid.hash);
 	for (i = 0; i < state->nr_written; i++)
 		free(state->written[i]);
 
diff --git a/cache.h b/cache.h
index badf3da..4f55466 100644
--- a/cache.h
+++ b/cache.h
@@ -43,6 +43,14 @@
 int git_deflate(git_zstream *, int flush);
 unsigned long git_deflate_bound(git_zstream *, unsigned long);
 
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+
+struct object_id {
+	unsigned char hash[GIT_SHA1_RAWSZ];
+};
+
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
 #define DTYPE(de)	((de)->d_type)
 #else
@@ -289,8 +297,11 @@
 #define RESOLVE_UNDO_CHANGED	(1 << 4)
 #define CACHE_TREE_CHANGED	(1 << 5)
 #define SPLIT_INDEX_ORDERED	(1 << 6)
+#define UNTRACKED_CHANGED	(1 << 7)
 
 struct split_index;
+struct untracked_cache;
+
 struct index_state {
 	struct cache_entry **cache;
 	unsigned int version;
@@ -304,6 +315,7 @@
 	struct hashmap name_hash;
 	struct hashmap dir_hash;
 	unsigned char sha1[20];
+	struct untracked_cache *untracked;
 };
 
 extern struct index_state the_index;
@@ -370,6 +382,7 @@
 
 /* Double-check local_repo_env below if you add to this list. */
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
 #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
@@ -423,11 +436,13 @@
 extern char *git_work_tree_cfg;
 extern int is_inside_work_tree(void);
 extern const char *get_git_dir(void);
+extern const char *get_git_common_dir(void);
 extern int is_git_directory(const char *path);
 extern char *get_object_directory(void);
 extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
+extern int get_common_dir(struct strbuf *sb, const char *gitdir);
 extern const char *get_git_namespace(void);
 extern const char *strip_namespace(const char *namespaced_ref);
 extern const char *get_git_work_tree(void);
@@ -552,6 +567,8 @@
  * INODE_CHANGED, and DATA_CHANGED.
  */
 extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+extern int match_stat_data_racy(const struct index_state *istate,
+				const struct stat_data *sd, struct stat *st);
 
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
@@ -612,6 +629,7 @@
 extern int precomposed_unicode;
 extern int protect_hfs;
 extern int protect_ntfs;
+extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
 
 /*
  * Include broken refs in all ref iterations, which will
@@ -682,18 +700,19 @@
 
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
-extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
-	__attribute__((format (printf, 3, 4)));
+extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+	__attribute__((format (printf, 2, 3)));
 extern char *git_pathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 extern char *mkpathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /* Return a statically allocated filename matching the sha1 signature */
-extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path_submodule(const char *path, const char *fmt, ...)
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path_submodule(const char *path, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
+extern void report_linked_checkout_garbage(void);
 
 /*
  * Return the name of the file in the local object database that would
@@ -718,13 +737,13 @@
 extern char *sha1_pack_index_name(const unsigned char *sha1);
 
 extern const char *find_unique_abbrev(const unsigned char *sha1, int);
-extern const unsigned char null_sha1[20];
+extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
 
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
 	int i;
 
-	for (i = 0; i < 20; i++, sha1++, sha2++) {
+	for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
 		if (*sha1 != *sha2)
 			return *sha1 - *sha2;
 	}
@@ -732,20 +751,42 @@
 	return 0;
 }
 
+static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
+{
+	return hashcmp(oid1->hash, oid2->hash);
+}
+
 static inline int is_null_sha1(const unsigned char *sha1)
 {
 	return !hashcmp(sha1, null_sha1);
 }
 
+static inline int is_null_oid(const struct object_id *oid)
+{
+	return !hashcmp(oid->hash, null_sha1);
+}
+
 static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
 {
-	memcpy(sha_dst, sha_src, 20);
+	memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
 }
+
+static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+{
+	hashcpy(dst->hash, src->hash);
+}
+
 static inline void hashclr(unsigned char *hash)
 {
-	memset(hash, 0, 20);
+	memset(hash, 0, GIT_SHA1_RAWSZ);
 }
 
+static inline void oidclr(struct object_id *oid)
+{
+	hashclr(oid->hash);
+}
+
+
 #define EMPTY_TREE_SHA1_HEX \
 	"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 #define EMPTY_TREE_SHA1_BIN_LITERAL \
@@ -844,6 +885,7 @@
 
 /* object replacement */
 #define LOOKUP_REPLACE_OBJECT 1
+#define LOOKUP_UNKNOWN_OBJECT 2
 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
 static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 {
@@ -901,8 +943,17 @@
  * Return true iff we have an object named sha1, whether local or in
  * an alternate object database, and whether packed or loose.  This
  * function does not respect replace references.
+ *
+ * If the QUICK flag is set, do not re-check the pack directory
+ * when we cannot find the object (this means we may give a false
+ * negative answer if another process is simultaneously repacking).
  */
-extern int has_sha1_file(const unsigned char *sha1);
+#define HAS_SHA1_QUICK 0x1
+extern int has_sha1_file_with_flags(const unsigned char *sha1, int flags);
+static inline int has_sha1_file(const unsigned char *sha1)
+{
+	return has_sha1_file_with_flags(sha1, 0);
+}
 
 /*
  * Return true iff an alternate object database has a loose object
@@ -929,15 +980,21 @@
 	unsigned char tree[20];
 	char path[PATH_MAX];
 	unsigned mode;
+	/*
+	 * symlink_path is only used by get_tree_entry_follow_symlinks,
+	 * and only for symlinks that point outside the repository.
+	 */
+	struct strbuf symlink_path;
 };
 
-#define GET_SHA1_QUIETLY        01
-#define GET_SHA1_COMMIT         02
-#define GET_SHA1_COMMITTISH     04
-#define GET_SHA1_TREE          010
-#define GET_SHA1_TREEISH       020
-#define GET_SHA1_BLOB	       040
-#define GET_SHA1_ONLY_TO_DIE 04000
+#define GET_SHA1_QUIETLY           01
+#define GET_SHA1_COMMIT            02
+#define GET_SHA1_COMMITTISH        04
+#define GET_SHA1_TREE             010
+#define GET_SHA1_TREEISH          020
+#define GET_SHA1_BLOB             040
+#define GET_SHA1_FOLLOW_SYMLINKS 0100
+#define GET_SHA1_ONLY_TO_DIE    04000
 
 extern int get_sha1(const char *str, unsigned char *sha1);
 extern int get_sha1_commit(const char *str, unsigned char *sha1);
@@ -959,8 +1016,10 @@
  * null-terminated string.
  */
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
+extern int get_oid_hex(const char *hex, struct object_id *sha1);
 
 extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
+extern char *oid_to_hex(const struct object_id *oid);	/* same static buffer as sha1_to_hex */
 extern int read_ref_full(const char *refname, int resolve_flags,
 			 unsigned char *sha1, int *flags);
 extern int read_ref(const char *refname, unsigned char *sha1);
@@ -1314,6 +1373,7 @@
 	unsigned long *sizep;
 	unsigned long *disk_sizep;
 	unsigned char *delta_base_sha1;
+	struct strbuf *typename;
 
 	/* Response */
 	enum {
@@ -1502,9 +1562,13 @@
 extern void maybe_flush_or_die(FILE *, const char *);
 __attribute__((format (printf, 2, 3)))
 extern void fprintf_or_die(FILE *, const char *fmt, ...);
+
+#define COPY_READ_ERROR (-2)
+#define COPY_WRITE_ERROR (-3)
 extern int copy_fd(int ifd, int ofd);
 extern int copy_file(const char *dst, const char *src, int mode);
 extern int copy_file_with_time(const char *dst, const char *src, int mode);
+
 extern void write_or_die(int fd, const void *buf, size_t count);
 extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
@@ -1518,6 +1582,8 @@
 {
 	return write_in_full(fd, str, strlen(str));
 }
+__attribute__((format (printf, 3, 4)))
+extern int write_file(const char *path, int fatal, const char *fmt, ...);
 
 /* pager.c */
 extern void setup_pager(void);
@@ -1641,5 +1707,6 @@
 void stat_validity_update(struct stat_validity *sv, int fd);
 
 int versioncmp(const char *s1, const char *s2);
+void sleep_millisec(int millisec);
 
 #endif /* CACHE_H */
diff --git a/combine-diff.c b/combine-diff.c
index d777e92..30c7eb6 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -44,9 +44,9 @@
 			memset(p->parent, 0,
 			       sizeof(p->parent[0]) * num_parent);
 
-			hashcpy(p->sha1, q->queue[i]->two->sha1);
+			hashcpy(p->oid.hash, q->queue[i]->two->sha1);
 			p->mode = q->queue[i]->two->mode;
-			hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
+			hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
 			p->parent[n].mode = q->queue[i]->one->mode;
 			p->parent[n].status = q->queue[i]->status;
 			*tail = p;
@@ -77,7 +77,7 @@
 			continue;
 		}
 
-		hashcpy(p->parent[n].sha1, q->queue[i]->one->sha1);
+		hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1);
 		p->parent[n].mode = q->queue[i]->one->mode;
 		p->parent[n].status = q->queue[i]->status;
 
@@ -284,7 +284,7 @@
 	return base;
 }
 
-static char *grab_blob(const unsigned char *sha1, unsigned int mode,
+static char *grab_blob(const struct object_id *oid, unsigned int mode,
 		       unsigned long *size, struct userdiff_driver *textconv,
 		       const char *path)
 {
@@ -294,20 +294,20 @@
 	if (S_ISGITLINK(mode)) {
 		blob = xmalloc(100);
 		*size = snprintf(blob, 100,
-				 "Subproject commit %s\n", sha1_to_hex(sha1));
-	} else if (is_null_sha1(sha1)) {
+				 "Subproject commit %s\n", oid_to_hex(oid));
+	} else if (is_null_oid(oid)) {
 		/* deleted blob */
 		*size = 0;
 		return xcalloc(1, 1);
 	} else if (textconv) {
 		struct diff_filespec *df = alloc_filespec(path);
-		fill_filespec(df, sha1, 1, mode);
+		fill_filespec(df, oid->hash, 1, mode);
 		*size = fill_textconv(textconv, df, &blob);
 		free_filespec(df);
 	} else {
-		blob = read_sha1_file(sha1, &type, size);
+		blob = read_sha1_file(oid->hash, &type, size);
 		if (type != OBJ_BLOB)
-			die("object '%s' is not a blob!", sha1_to_hex(sha1));
+			die("object '%s' is not a blob!", oid_to_hex(oid));
 	}
 	return blob;
 }
@@ -389,7 +389,7 @@
 	}
 }
 
-static void combine_diff(const unsigned char *parent, unsigned int mode,
+static void combine_diff(const struct object_id *parent, unsigned int mode,
 			 mmfile_t *result_file,
 			 struct sline *sline, unsigned int cnt, int n,
 			 int num_parent, int result_deleted,
@@ -897,7 +897,7 @@
 				 int show_file_header)
 {
 	struct diff_options *opt = &rev->diffopt;
-	int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+	int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
 	const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
 	const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
 	const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
@@ -914,11 +914,11 @@
 			 "", elem->path, line_prefix, c_meta, c_reset);
 	printf("%s%sindex ", line_prefix, c_meta);
 	for (i = 0; i < num_parent; i++) {
-		abb = find_unique_abbrev(elem->parent[i].sha1,
+		abb = find_unique_abbrev(elem->parent[i].oid.hash,
 					 abbrev);
 		printf("%s%s", i ? "," : "", abb);
 	}
-	abb = find_unique_abbrev(elem->sha1, abbrev);
+	abb = find_unique_abbrev(elem->oid.hash, abbrev);
 	printf("..%s%s\n", abb, c_reset);
 
 	if (mode_differs) {
@@ -991,7 +991,7 @@
 
 	/* Read the result of merge first */
 	if (!working_tree_file)
-		result = grab_blob(elem->sha1, elem->mode, &result_size,
+		result = grab_blob(&elem->oid, elem->mode, &result_size,
 				   textconv, elem->path);
 	else {
 		/* Used by diff-tree to read from the working tree */
@@ -1013,12 +1013,12 @@
 			result = strbuf_detach(&buf, NULL);
 			elem->mode = canon_mode(st.st_mode);
 		} else if (S_ISDIR(st.st_mode)) {
-			unsigned char sha1[20];
-			if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0)
-				result = grab_blob(elem->sha1, elem->mode,
+			struct object_id oid;
+			if (resolve_gitlink_ref(elem->path, "HEAD", oid.hash) < 0)
+				result = grab_blob(&elem->oid, elem->mode,
 						   &result_size, NULL, NULL);
 			else
-				result = grab_blob(sha1, elem->mode,
+				result = grab_blob(&oid, elem->mode,
 						   &result_size, NULL, NULL);
 		} else if (textconv) {
 			struct diff_filespec *df = alloc_filespec(elem->path);
@@ -1090,7 +1090,7 @@
 		for (i = 0; !is_binary && i < num_parent; i++) {
 			char *buf;
 			unsigned long size;
-			buf = grab_blob(elem->parent[i].sha1,
+			buf = grab_blob(&elem->parent[i].oid,
 					elem->parent[i].mode,
 					&size, NULL, NULL);
 			if (buffer_is_binary(buf, size))
@@ -1139,14 +1139,14 @@
 	for (i = 0; i < num_parent; i++) {
 		int j;
 		for (j = 0; j < i; j++) {
-			if (!hashcmp(elem->parent[i].sha1,
-				     elem->parent[j].sha1)) {
+			if (!oidcmp(&elem->parent[i].oid,
+				     &elem->parent[j].oid)) {
 				reuse_combine_diff(sline, cnt, i, j);
 				break;
 			}
 		}
 		if (i <= j)
-			combine_diff(elem->parent[i].sha1,
+			combine_diff(&elem->parent[i].oid,
 				     elem->parent[i].mode,
 				     &result_file, sline,
 				     cnt, i, num_parent, result_deleted,
@@ -1206,9 +1206,9 @@
 
 		/* Show sha1's */
 		for (i = 0; i < num_parent; i++)
-			printf(" %s", diff_unique_abbrev(p->parent[i].sha1,
+			printf(" %s", diff_unique_abbrev(p->parent[i].oid.hash,
 							 opt->abbrev));
-		printf(" %s ", diff_unique_abbrev(p->sha1, opt->abbrev));
+		printf(" %s ", diff_unique_abbrev(p->oid.hash, opt->abbrev));
 	}
 
 	if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
@@ -1271,16 +1271,16 @@
 	for (i = 0; i < num_parent; i++) {
 		pair->one[i].path = p->path;
 		pair->one[i].mode = p->parent[i].mode;
-		hashcpy(pair->one[i].sha1, p->parent[i].sha1);
-		pair->one[i].sha1_valid = !is_null_sha1(p->parent[i].sha1);
+		hashcpy(pair->one[i].sha1, p->parent[i].oid.hash);
+		pair->one[i].sha1_valid = !is_null_oid(&p->parent[i].oid);
 		pair->one[i].has_more_entries = 1;
 	}
 	pair->one[num_parent - 1].has_more_entries = 0;
 
 	pair->two->path = p->path;
 	pair->two->mode = p->mode;
-	hashcpy(pair->two->sha1, p->sha1);
-	pair->two->sha1_valid = !is_null_sha1(p->sha1);
+	hashcpy(pair->two->sha1, p->oid.hash);
+	pair->two->sha1_valid = !is_null_oid(&p->oid);
 	return pair;
 }
 
diff --git a/command-list.txt b/command-list.txt
index f1eae08..b17c011 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -1,29 +1,39 @@
-# List of known git commands.
-# command name				category [deprecated] [common]
-git-add                                 mainporcelain common
+# common commands are grouped by themes
+# these groups are output by 'git help' in the order declared here.
+# map each common command in the command list to one of these groups.
+### common groups (do not change this line)
+init         start a working area (see also: git help tutorial)
+worktree     work on the current change (see also: git help everyday)
+info         examine the history and state (see also: git help revisions)
+history      grow, mark and tweak your common history
+remote       collaborate (see also: git help workflows)
+
+### command list (do not change this line)
+# command name                          category [deprecated] [common]
+git-add                                 mainporcelain           worktree
 git-am                                  mainporcelain
 git-annotate                            ancillaryinterrogators
 git-apply                               plumbingmanipulators
 git-archimport                          foreignscminterface
 git-archive                             mainporcelain
-git-bisect                              mainporcelain common
+git-bisect                              mainporcelain           info
 git-blame                               ancillaryinterrogators
-git-branch                              mainporcelain common
+git-branch                              mainporcelain           history
 git-bundle                              mainporcelain
 git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
 git-check-mailmap                       purehelpers
-git-checkout                            mainporcelain common
+git-checkout                            mainporcelain           history
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
 git-cherry                              ancillaryinterrogators
 git-cherry-pick                         mainporcelain
 git-citool                              mainporcelain
 git-clean                               mainporcelain
-git-clone                               mainporcelain common
+git-clone                               mainporcelain           init
 git-column                              purehelpers
-git-commit                              mainporcelain common
+git-commit                              mainporcelain           history
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
 git-count-objects                       ancillaryinterrogators
@@ -35,42 +45,42 @@
 git-cvsserver                           foreignscminterface
 git-daemon                              synchingrepositories
 git-describe                            mainporcelain
-git-diff                                mainporcelain common
+git-diff                                mainporcelain           history
 git-diff-files                          plumbinginterrogators
 git-diff-index                          plumbinginterrogators
 git-diff-tree                           plumbinginterrogators
 git-difftool                            ancillaryinterrogators
-git-fast-export				ancillarymanipulators
-git-fast-import				ancillarymanipulators
-git-fetch                               mainporcelain common
+git-fast-export                         ancillarymanipulators
+git-fast-import                         ancillarymanipulators
+git-fetch                               mainporcelain           remote
 git-fetch-pack                          synchingrepositories
 git-filter-branch                       ancillarymanipulators
 git-fmt-merge-msg                       purehelpers
 git-for-each-ref                        plumbinginterrogators
 git-format-patch                        mainporcelain
-git-fsck	                        ancillaryinterrogators
+git-fsck                                ancillaryinterrogators
 git-gc                                  mainporcelain
 git-get-tar-commit-id                   ancillaryinterrogators
-git-grep                                mainporcelain common
+git-grep                                mainporcelain           info
 git-gui                                 mainporcelain
 git-hash-object                         plumbingmanipulators
-git-help				ancillaryinterrogators
+git-help                                ancillaryinterrogators
 git-http-backend                        synchingrepositories
 git-http-fetch                          synchelpers
 git-http-push                           synchelpers
 git-imap-send                           foreignscminterface
 git-index-pack                          plumbingmanipulators
-git-init                                mainporcelain common
+git-init                                mainporcelain           init
 git-instaweb                            ancillaryinterrogators
 git-interpret-trailers                  purehelpers
 gitk                                    mainporcelain
-git-log                                 mainporcelain common
+git-log                                 mainporcelain           info
 git-ls-files                            plumbinginterrogators
 git-ls-remote                           plumbinginterrogators
 git-ls-tree                             plumbinginterrogators
 git-mailinfo                            purehelpers
 git-mailsplit                           purehelpers
-git-merge                               mainporcelain common
+git-merge                               mainporcelain           history
 git-merge-base                          plumbinginterrogators
 git-merge-file                          plumbingmanipulators
 git-merge-index                         plumbingmanipulators
@@ -79,7 +89,7 @@
 git-merge-tree                          ancillaryinterrogators
 git-mktag                               plumbingmanipulators
 git-mktree                              plumbingmanipulators
-git-mv                                  mainporcelain common
+git-mv                                  mainporcelain           worktree
 git-name-rev                            plumbinginterrogators
 git-notes                               mainporcelain
 git-p4                                  foreignscminterface
@@ -90,11 +100,11 @@
 git-patch-id                            purehelpers
 git-prune                               ancillarymanipulators
 git-prune-packed                        plumbingmanipulators
-git-pull                                mainporcelain common
-git-push                                mainporcelain common
+git-pull                                mainporcelain           remote
+git-push                                mainporcelain           remote
 git-quiltimport                         foreignscminterface
 git-read-tree                           plumbingmanipulators
-git-rebase                              mainporcelain common
+git-rebase                              mainporcelain           history
 git-receive-pack                        synchelpers
 git-reflog                              ancillarymanipulators
 git-relink                              ancillarymanipulators
@@ -103,28 +113,28 @@
 git-replace                             ancillarymanipulators
 git-request-pull                        foreignscminterface
 git-rerere                              ancillaryinterrogators
-git-reset                               mainporcelain common
+git-reset                               mainporcelain           worktree
 git-revert                              mainporcelain
 git-rev-list                            plumbinginterrogators
 git-rev-parse                           ancillaryinterrogators
-git-rm                                  mainporcelain common
+git-rm                                  mainporcelain           worktree
 git-send-email                          foreignscminterface
 git-send-pack                           synchingrepositories
 git-shell                               synchelpers
 git-shortlog                            mainporcelain
-git-show                                mainporcelain common
+git-show                                mainporcelain           info
 git-show-branch                         ancillaryinterrogators
 git-show-index                          plumbinginterrogators
 git-show-ref                            plumbinginterrogators
 git-sh-i18n                             purehelpers
 git-sh-setup                            purehelpers
 git-stash                               mainporcelain
-git-status                              mainporcelain common
+git-status                              mainporcelain           info
 git-stripspace                          purehelpers
 git-submodule                           mainporcelain
 git-svn                                 foreignscminterface
 git-symbolic-ref                        plumbingmanipulators
-git-tag                                 mainporcelain common
+git-tag                                 mainporcelain           history
 git-unpack-file                         plumbinginterrogators
 git-unpack-objects                      plumbingmanipulators
 git-update-index                        plumbingmanipulators
diff --git a/commit.c b/commit.c
index 0eee410..6e2103c 100644
--- a/commit.c
+++ b/commit.c
@@ -55,12 +55,12 @@
 
 struct commit *lookup_commit_reference_by_name(const char *name)
 {
-	unsigned char sha1[20];
+	struct object_id oid;
 	struct commit *commit;
 
-	if (get_sha1_committish(name, sha1))
+	if (get_sha1_committish(name, oid.hash))
 		return NULL;
-	commit = lookup_commit_reference(sha1);
+	commit = lookup_commit_reference(oid.hash);
 	if (parse_commit(commit))
 		return NULL;
 	return commit;
@@ -99,7 +99,7 @@
 static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
 {
 	struct commit_graft **commit_graft_table = table;
-	return commit_graft_table[index]->sha1;
+	return commit_graft_table[index]->oid.hash;
 }
 
 static int commit_graft_pos(const unsigned char *sha1)
@@ -110,7 +110,7 @@
 
 int register_commit_graft(struct commit_graft *graft, int ignore_dups)
 {
-	int pos = commit_graft_pos(graft->sha1);
+	int pos = commit_graft_pos(graft->oid.hash);
 
 	if (0 <= pos) {
 		if (ignore_dups)
@@ -138,22 +138,23 @@
 	/* The format is just "Commit Parent1 Parent2 ...\n" */
 	int i;
 	struct commit_graft *graft = NULL;
+	const int entry_size = GIT_SHA1_HEXSZ + 1;
 
 	while (len && isspace(buf[len-1]))
 		buf[--len] = '\0';
 	if (buf[0] == '#' || buf[0] == '\0')
 		return NULL;
-	if ((len + 1) % 41)
+	if ((len + 1) % entry_size)
 		goto bad_graft_data;
-	i = (len + 1) / 41 - 1;
-	graft = xmalloc(sizeof(*graft) + 20 * i);
+	i = (len + 1) / entry_size - 1;
+	graft = xmalloc(sizeof(*graft) + GIT_SHA1_RAWSZ * i);
 	graft->nr_parent = i;
-	if (get_sha1_hex(buf, graft->sha1))
+	if (get_oid_hex(buf, &graft->oid))
 		goto bad_graft_data;
-	for (i = 40; i < len; i += 41) {
+	for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {
 		if (buf[i] != ' ')
 			goto bad_graft_data;
-		if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
+		if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))
 			goto bad_graft_data;
 	}
 	return graft;
@@ -302,39 +303,42 @@
 {
 	const char *tail = buffer;
 	const char *bufptr = buffer;
-	unsigned char parent[20];
+	struct object_id parent;
 	struct commit_list **pptr;
 	struct commit_graft *graft;
+	const int tree_entry_len = GIT_SHA1_HEXSZ + 5;
+	const int parent_entry_len = GIT_SHA1_HEXSZ + 7;
 
 	if (item->object.parsed)
 		return 0;
 	item->object.parsed = 1;
 	tail += size;
-	if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n')
+	if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
+			bufptr[tree_entry_len] != '\n')
 		return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
-	if (get_sha1_hex(bufptr + 5, parent) < 0)
+	if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
 		return error("bad tree pointer in commit %s",
 			     sha1_to_hex(item->object.sha1));
-	item->tree = lookup_tree(parent);
-	bufptr += 46; /* "tree " + "hex sha1" + "\n" */
+	item->tree = lookup_tree(parent.hash);
+	bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
 	pptr = &item->parents;
 
 	graft = lookup_commit_graft(item->object.sha1);
-	while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) {
+	while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
 		struct commit *new_parent;
 
-		if (tail <= bufptr + 48 ||
-		    get_sha1_hex(bufptr + 7, parent) ||
-		    bufptr[47] != '\n')
+		if (tail <= bufptr + parent_entry_len + 1 ||
+		    get_sha1_hex(bufptr + 7, parent.hash) ||
+		    bufptr[parent_entry_len] != '\n')
 			return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
-		bufptr += 48;
+		bufptr += parent_entry_len + 1;
 		/*
 		 * The clone is shallow if nr_parent < 0, and we must
 		 * not traverse its real parents even when we unhide them.
 		 */
 		if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
 			continue;
-		new_parent = lookup_commit(parent);
+		new_parent = lookup_commit(parent.hash);
 		if (new_parent)
 			pptr = &commit_list_insert(new_parent, pptr)->next;
 	}
@@ -342,7 +346,7 @@
 		int i;
 		struct commit *new_parent;
 		for (i = 0; i < graft->nr_parent; i++) {
-			new_parent = lookup_commit(graft->parent[i]);
+			new_parent = lookup_commit(graft->parent[i].hash);
 			if (!new_parent)
 				continue;
 			pptr = &commit_list_insert(new_parent, pptr)->next;
@@ -1581,10 +1585,10 @@
 {
 	struct object *obj;
 	struct commit *commit;
-	unsigned char sha1[20];
-	if (get_sha1(name, sha1))
+	struct object_id oid;
+	if (get_sha1(name, oid.hash))
 		return NULL;
-	obj = parse_object(sha1);
+	obj = parse_object(oid.hash);
 	commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
 	if (commit && !commit->util) {
 		struct merge_remote_desc *desc;
diff --git a/commit.h b/commit.h
index 4789839..9a1fa96 100644
--- a/commit.h
+++ b/commit.h
@@ -230,9 +230,9 @@
 void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
 
 struct commit_graft {
-	unsigned char sha1[20];
+	struct object_id oid;
 	int nr_parent; /* < 0 if shallow commit */
-	unsigned char parent[FLEX_ARRAY][20]; /* more */
+	struct object_id parent[FLEX_ARRAY]; /* more */
 };
 typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
diff --git a/compat/mingw.c b/compat/mingw.c
index 70f3191..496e6f8 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2128,3 +2128,14 @@
 	/* initialize Unicode console */
 	winansi_init();
 }
+
+int uname(struct utsname *buf)
+{
+	DWORD v = GetVersion();
+	memset(buf, 0, sizeof(*buf));
+	strcpy(buf->sysname, "Windows");
+	sprintf(buf->release, "%u.%u", v & 0xff, (v >> 8) & 0xff);
+	/* assuming NT variants only.. */
+	sprintf(buf->version, "%u", (v >> 16) & 0x7fff);
+	return 0;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index 5e499cf..738865c 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -76,6 +76,14 @@
 };
 #define ITIMER_REAL 0
 
+struct utsname {
+	char sysname[16];
+	char nodename[1];
+	char release[16];
+	char version[16];
+	char machine[1];
+};
+
 /*
  * sanitize preprocessor namespace polluted by Windows headers defining
  * macros which collide with git local versions
@@ -98,8 +106,6 @@
 { return 0; }
 static inline int fsync(int fd)
 { return _commit(fd); }
-static inline pid_t getppid(void)
-{ return 1; }
 static inline void sync(void)
 {}
 static inline uid_t getuid(void)
@@ -121,6 +127,12 @@
 #define SIG_UNBLOCK 0
 static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
 { return 0; }
+static inline pid_t getppid(void)
+{ return 1; }
+static inline pid_t getpgid(pid_t pid)
+{ return pid == 0 ? getpid() : pid; }
+static inline pid_t tcgetpgrp(int fd)
+{ return getpid(); }
 
 /*
  * simple adaptors
@@ -171,6 +183,7 @@
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
+int uname(struct utsname *buf);
 
 /*
  * replacements of existing functions
diff --git a/config.c b/config.c
index 27a73c8..9fd275f 100644
--- a/config.c
+++ b/config.c
@@ -50,7 +50,7 @@
 
 static int config_file_fgetc(struct config_source *conf)
 {
-	return fgetc(conf->u.file);
+	return getc_unlocked(conf->u.file);
 }
 
 static int config_file_ungetc(int c, struct config_source *conf)
@@ -1088,7 +1088,9 @@
 
 	f = fopen(filename, "r");
 	if (f) {
+		flockfile(f);
 		ret = do_config_from_file(fn, filename, filename, f, data);
+		funlockfile(f);
 		fclose(f);
 	}
 	return ret;
@@ -2116,6 +2118,9 @@
 					  contents_sz - copy_begin) <
 			    contents_sz - copy_begin)
 				goto write_err_out;
+
+		munmap(contents, contents_sz);
+		contents = NULL;
 	}
 
 	if (commit_lock_file(lock) < 0) {
diff --git a/config.mak.uname b/config.mak.uname
index f4e77cb..943c439 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -36,6 +36,7 @@
 	HAVE_DEV_TTY = YesPlease
 	HAVE_CLOCK_GETTIME = YesPlease
 	HAVE_CLOCK_MONOTONIC = YesPlease
+	HAVE_GETDELIM = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	HAVE_ALLOCA_H = YesPlease
@@ -101,6 +102,9 @@
 	ifeq ($(shell expr "$(uname_R)" : '[15]\.'),2)
 		NO_STRLCPY = YesPlease
 	endif
+	ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
+		HAVE_GETDELIM = YesPlease
+	endif
 	NO_MEMMEM = YesPlease
 	USE_ST_TIMESPEC = YesPlease
 	HAVE_DEV_TTY = YesPlease
diff --git a/configure.ac b/configure.ac
index bbdde85..14012fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1041,6 +1041,12 @@
 [NO_INITGROUPS=YesPlease])
 GIT_CONF_SUBST([NO_INITGROUPS])
 #
+# Define HAVE_GETDELIM if you have getdelim in the C library.
+GIT_CHECK_FUNC(getdelim,
+[HAVE_GETDELIM=YesPlease],
+[HAVE_GETDELIM=])
+GIT_CONF_SUBST([HAVE_GETDELIM])
+#
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index eae9dce..c97c648 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -665,8 +665,8 @@
 		checkout-index)   : plumbing;;
 		commit-tree)      : plumbing;;
 		count-objects)    : infrequent;;
-		credential-cache) : credentials helper;;
-		credential-store) : credentials helper;;
+		credential)       : credentials;;
+		credential-*)     : credentials helper;;
 		cvsexportcommit)  : export;;
 		cvsimport)        : import;;
 		cvsserver)        : daemon;;
@@ -735,35 +735,29 @@
 __git_porcelain_commands=
 __git_compute_porcelain_commands ()
 {
-	__git_compute_all_commands
 	test -n "$__git_porcelain_commands" ||
 	__git_porcelain_commands=$(__git_list_porcelain_commands)
 }
 
+# Lists all set config variables starting with the given section prefix,
+# with the prefix removed.
+__git_get_config_variables ()
+{
+	local section="$1" i IFS=$'\n'
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "^$section\..*" 2>/dev/null); do
+		i="${i#$section.}"
+		echo "${i/ */}"
+	done
+}
+
 __git_pretty_aliases ()
 {
-	local i IFS=$'\n'
-	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "pretty\..*" 2>/dev/null); do
-		case "$i" in
-		pretty.*)
-			i="${i#pretty.}"
-			echo "${i/ */}"
-			;;
-		esac
-	done
+	__git_get_config_variables "pretty"
 }
 
 __git_aliases ()
 {
-	local i IFS=$'\n'
-	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
-		case "$i" in
-		alias.*)
-			i="${i#alias.}"
-			echo "${i/ */}"
-			;;
-		esac
-	done
+	__git_get_config_variables "alias"
 }
 
 # __git_aliased_command requires 1 argument
@@ -1114,7 +1108,7 @@
 
 	case "$cur" in
 	--cleanup=*)
-		__gitcomp "default strip verbatim whitespace
+		__gitcomp "default scissors strip verbatim whitespace
 			" "" "${cur##--cleanup=}"
 		return
 		;;
@@ -2123,6 +2117,7 @@
 		http.noEPSV
 		http.postBuffer
 		http.proxy
+		http.sslCipherList
 		http.sslCAInfo
 		http.sslCAPath
 		http.sslCert
@@ -2260,12 +2255,7 @@
 		__git_complete_remote_or_refspec
 		;;
 	update)
-		local i c='' IFS=$'\n'
-		for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
-			i="${i#remotes.}"
-			c="$c ${i/ */}"
-		done
-		__gitcomp "$c"
+		__gitcomp "$(__git_get_config_variables "remotes")"
 		;;
 	*)
 		;;
@@ -2292,6 +2282,11 @@
 
 _git_revert ()
 {
+	local dir="$(__gitdir)"
+	if [ -f "$dir"/REVERT_HEAD ]; then
+		__gitcomp "--continue --quit --abort"
+		return
+	fi
 	case "$cur" in
 	--*)
 		__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh
index 6104a42..4a790d8 100644
--- a/contrib/completion/git-completion.tcsh
+++ b/contrib/completion/git-completion.tcsh
@@ -41,7 +41,7 @@
 	exit
 endif
 
-cat << EOF > ${__git_tcsh_completion_script}
+cat << EOF >! ${__git_tcsh_completion_script}
 #!bash
 #
 # This script is GENERATED and will be overwritten automatically.
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index f18aedc..366f0bc 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -66,6 +66,10 @@
 #     git           always compare HEAD to @{upstream}
 #     svn           always compare HEAD to your SVN upstream
 #
+# You can change the separator between the branch name and the above
+# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator
+# is SP.
+#
 # By default, __git_ps1 will compare HEAD to your SVN upstream if it can
 # find one, or @{upstream} otherwise.  Once you have set
 # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
diff --git a/contrib/hooks/multimail/CHANGES b/contrib/hooks/multimail/CHANGES
index 3603d56..6bb1230 100644
--- a/contrib/hooks/multimail/CHANGES
+++ b/contrib/hooks/multimail/CHANGES
@@ -1,3 +1,56 @@
+Release 1.1.1 (bugfix-only release)
+===================================
+
+* The SMTP mailer was not working with Python 2.4.
+
+Release 1.1.0
+=============
+
+* When a single commit is pushed, omit the reference changed email.
+  Set multimailhook.combineWhenSingleCommit to false to disable this
+  new feature.
+
+* In gitolite environments, the pusher's email address can be used as
+  the From address by creating a specially formatted comment block in
+  gitolite.conf (see multimailhook.from in README).
+
+* Support for SMTP authentication and SSL/TLS encryption was added,
+  see smtpUser, smtpPass, smtpEncryption in README.
+
+* A new option scanCommitForCc was added to allow git-multimail to
+  search the commit message for 'Cc: ...' lines, and add the
+  corresponding emails in Cc.
+
+* If $USER is not set, use the variable $USERNAME. This is needed on
+  Windows platform to recognize the pusher.
+
+* The emailPrefix variable can now be set to an empty string to remove
+  the prefix.
+
+* A short tutorial was added in doc/gitolite.rst to set up
+  git-multimail with gitolite.
+
+* The post-receive file was renamed to post-receive.example. It has
+  always been an example (the standard way to call git-multimail is to
+  call git_multimail.py), but it was unclear to many users.
+
+* A new refchangeShowGraph option was added to make it possible to
+  include both a graph and a log in the summary emails.  The options
+  to control the graph formatting can be set via the new graphOpts
+  option.
+
+* New option --force-send was added to disable new commit detection
+  for update hook. One use-case is to run git_multimail.py after
+  running "git fetch" to send emails about commits that have just been
+  fetched (the detection of new commits was unreliable in this mode).
+
+* The testing infrastructure was considerably improved (continuous
+  integration with travis-ci, automatic check of PEP8 and RST syntax,
+  many improvements to the test scripts).
+
+This version has been tested with Python 2.4 to 2.7, and Git 1.7.1 to
+2.4.
+
 Release 1.0.0
 =============
 
diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README
index 6efa4ff..e552c90 100644
--- a/contrib/hooks/multimail/README
+++ b/contrib/hooks/multimail/README
@@ -1,5 +1,8 @@
-			   git-multimail
-			   =============
+git-multimail Version 1.1.1
+===========================
+
+.. image:: https://travis-ci.org/git-multimail/git-multimail.svg?branch=master
+    :target: https://travis-ci.org/git-multimail/git-multimail
 
 git-multimail is a tool for sending notification emails on pushes to a
 Git repository.  It includes a Python module called git_multimail.py,
@@ -38,17 +41,17 @@
    list) makes it easy to scan through the emails, jump to patches
    that need further attention, and write comments about specific
    commits.  Commits are handled in reverse topological order (i.e.,
-   parents shown before children).  For example,
+   parents shown before children).  For example::
 
-   [git] branch master updated
-   + [git] 01/08: doc: fix xref link from api docs to manual pages
-   + [git] 02/08: api-credentials.txt: show the big picture first
-   + [git] 03/08: api-credentials.txt: mention credential.helper explicitly
-   + [git] 04/08: api-credentials.txt: add "see also" section
-   + [git] 05/08: t3510 (cherry-pick-sequence): add missing '&&'
-   + [git] 06/08: Merge branch 'rr/maint-t3510-cascade-fix'
-   + [git] 07/08: Merge branch 'mm/api-credentials-doc'
-   + [git] 08/08: Git 1.7.11-rc2
+     [git] branch master updated
+     + [git] 01/08: doc: fix xref link from api docs to manual pages
+     + [git] 02/08: api-credentials.txt: show the big picture first
+     + [git] 03/08: api-credentials.txt: mention credential.helper explicitly
+     + [git] 04/08: api-credentials.txt: add "see also" section
+     + [git] 05/08: t3510 (cherry-pick-sequence): add missing '&&'
+     + [git] 06/08: Merge branch 'rr/maint-t3510-cascade-fix'
+     + [git] 07/08: Merge branch 'mm/api-credentials-doc'
+     + [git] 08/08: Git 1.7.11-rc2
 
    Each commit appears in exactly one commit email, the first time
    that it is pushed to the repository.  If a commit is later merged
@@ -74,19 +77,19 @@
   3.x.
 
   The example scripts invoke Python using the following shebang line
-  (following PEP 394 [1]):
+  (following PEP 394 [1]_)::
 
       #! /usr/bin/env python2
 
   If your system's Python2 interpreter is not in your PATH or is not
-  called "python2", you can change the lines accordingly.  Or you can
+  called ``python2``, you can change the lines accordingly.  Or you can
   invoke the Python interpreter explicitly, for example via a tiny
-  shell script like
+  shell script like::
 
       #! /bin/sh
       /usr/local/bin/python /path/to/git_multimail.py "$@"
 
-* The "git" command must be in your PATH.  git-multimail is known to
+* The ``git`` command must be in your PATH.  git-multimail is known to
   work with Git versions back to 1.7.1.  (Earlier versions have not
   been tested; if you do so, please report your results.)
 
@@ -101,7 +104,7 @@
 Invocation
 ----------
 
-git_multimail.py is designed to be used as a "post-receive" hook in a
+git_multimail.py is designed to be used as a ``post-receive`` hook in a
 Git repository (see githooks(5)).  Link or copy it to
 $GIT_DIR/hooks/post-receive within the repository for which email
 notifications are desired.  Usually it should be installed on the
@@ -109,10 +112,10 @@
 pushed.
 
 For use on pre-v1.5.1 Git servers, git_multimail.py can also work as
-an "update" hook, taking its arguments on the command line.  To use
+an ``update`` hook, taking its arguments on the command line.  To use
 this script in this manner, link or copy it to $GIT_DIR/hooks/update.
 Please note that the script is not completely reliable in this mode
-[2].
+[2]_.
 
 Alternatively, git_multimail.py can be imported as a Python module
 into your own Python post-receive script.  This method is a bit more
@@ -129,7 +132,7 @@
   only about changes affecting particular files or subdirectories)
 
 Or you can change how emails are sent by writing your own Mailer
-class.  The "post-receive" script in this directory demonstrates how
+class.  The ``post-receive`` script in this directory demonstrates how
 to use git_multimail.py as a Python module.  (If you make interesting
 changes of this type, please consider sharing them with the
 community.)
@@ -139,18 +142,26 @@
 -------------
 
 By default, git-multimail mostly takes its configuration from the
-following "git config" settings:
+following ``git config`` settings:
 
 multimailhook.environment
 
     This describes the general environment of the repository.
     Currently supported values:
 
-    "generic" -- the username of the pusher is read from $USER and the
-        repository name is derived from the repository's path.
+    * generic
 
-    "gitolite" -- the username of the pusher is read from $GL_USER and
-        the repository name from $GL_REPO.
+      the username of the pusher is read from $USER or $USERNAME and
+      the repository name is derived from the repository's path.
+
+    * gitolite
+
+      the username of the pusher is read from $GL_USER, the repository
+      name is read from $GL_REPO, and the From: header value is
+      optionally read from gitolite.conf (see multimailhook.from).
+
+      For more information about gitolite and git-multimail, read
+      doc/gitolite.rst
 
     If neither of these environments is suitable for your setup, then
     you can implement a Python class that inherits from Environment
@@ -160,8 +171,8 @@
     The environment value can be specified on the command line using
     the --environment option.  If it is not specified on the command
     line or by multimailhook.environment, then it defaults to
-    "gitolite" if the environment contains variables $GL_USER and
-    $GL_REPO; otherwise "generic".
+    ``gitolite`` if the environment contains variables $GL_USER and
+    $GL_REPO; otherwise ``generic``.
 
 multimailhook.repoName
 
@@ -219,61 +230,109 @@
     not so straightforward, then the shortlog might be confusing
     rather than useful.  Default is false.
 
+multimailhook.refchangeShowGraph
+
+    If this option is set to true, then summary emails about reference
+    changes will additionally include:
+
+    * a graph of the added commits (if any)
+
+    * a graph of the discarded commits (if any)
+
+    The log is generated by running ``git log --graph`` with the options
+    specified in graphOpts.  The default is false.
+
 multimailhook.refchangeShowLog
 
     If this option is set to true, then summary emails about reference
     changes will include a detailed log of the added commits in
     addition to the one line summary.  The log is generated by running
-    "git log" with the options specified in multimailhook.logOpts.
+    ``git log`` with the options specified in multimailhook.logOpts.
     Default is false.
 
 multimailhook.mailer
 
     This option changes the way emails are sent.  Accepted values are:
 
-    - sendmail (the default): use the command /usr/sbin/sendmail or
-      /usr/lib/sendmail (or sendmailCommand, if configured).  This
+    - sendmail (the default): use the command ``/usr/sbin/sendmail`` or
+      ``/usr/lib/sendmail`` (or sendmailCommand, if configured).  This
       mode can be further customized via the following options:
 
-       multimailhook.sendmailCommand
+      * multimailhook.sendmailCommand
 
-           The command used by mailer "sendmail" to send emails.  Shell
-           quoting is allowed in the value of this setting, but remember that
-           Git requires double-quotes to be escaped; e.g.,
+        The command used by mailer ``sendmail`` to send emails.  Shell
+        quoting is allowed in the value of this setting, but remember that
+        Git requires double-quotes to be escaped; e.g.::
 
              git config multimailhook.sendmailcommand '/usr/sbin/sendmail -oi -t -F \"Git Repo\"'
 
-           Default is '/usr/sbin/sendmail -oi -t' or
-           '/usr/lib/sendmail -oi -t' (depending on which file is
-           present and executable).
+        Default is '/usr/sbin/sendmail -oi -t' or
+        '/usr/lib/sendmail -oi -t' (depending on which file is
+        present and executable).
 
-       multimailhook.envelopeSender
+      * multimailhook.envelopeSender
 
-           If set then pass this value to sendmail via the -f option to set
-           the envelope sender address.
+        If set then pass this value to sendmail via the -f option to set
+        the envelope sender address.
 
     - smtp: use Python's smtplib.  This is useful when the sendmail
       command is not available on the system.  This mode can be
       further customized via the following options:
 
-       multimailhook.smtpServer
+      * multimailhook.smtpServer
 
-           The name of the SMTP server to connect to.  The value can
-           also include a colon and a port number; e.g.,
-           "mail.example.com:25".  Default is 'localhost' using port
-           25.
+        The name of the SMTP server to connect to.  The value can
+        also include a colon and a port number; e.g.,
+        ``mail.example.com:25``.  Default is 'localhost' using port 25.
 
-       multimailhook.envelopeSender
+      * multimailhook.smtpUser
+      * multimailhook.smtpPass
 
-           The sender address to be passed to the SMTP server.  If
-           unset, then the value of multimailhook.from is used.
+        Server username and password. Required if smtpEncryption is 'ssl'.
+        Note that the username and password currently need to be
+        set cleartext in the configuration file, which is not
+        recommended. If you need to use this option, be sure your
+        configuration file is read-only.
+
+      * multimailhook.envelopeSender
+
+        The sender address to be passed to the SMTP server.  If
+        unset, then the value of multimailhook.from is used.
+
+      * multimailhook.smtpServerTimeout
+
+        Timeout in seconds.
+
+      * multimailhook.smtpEncryption
+
+        Set the security type. Allowed values: none, ssl.
+        Default=none.
+
+      * multimailhook.smtpServerDebugLevel
+
+        Integer number. Set to greater than 0 to activate debugging.
 
 multimailhook.from
 
-    If set then use this value in the From: field of generated emails.
-    If unset, then use the repository's user configuration (user.name
-    and user.email).  If user.email is also unset, then use
-    multimailhook.envelopeSender.
+    If set, use this value in the From: field of generated emails.  If
+    unset, the value of the From: header is determined as follows:
+
+    1. (gitolite environment only) Parse gitolite.conf, looking for a
+       block of comments that looks like this::
+
+           # BEGIN USER EMAILS
+           # username Firstname Lastname <email@example.com>
+           # END USER EMAILS
+
+       If that block exists, and there is a line between the BEGIN
+       USER EMAILS and END USER EMAILS lines where the first field
+       matches the gitolite username ($GL_USER), use the rest of the
+       line for the From: header.
+
+    2. If the user.email configuration setting is set, use its value
+       (and the value of user.name, if set).
+
+    3. Use the value of multimailhook.envelopeSender.
 
 multimailhook.administrator
 
@@ -287,7 +346,8 @@
     All emails have this string prepended to their subjects, to aid
     email filtering (though filtering based on the X-Git-* email
     headers is probably more robust).  Default is the short name of
-    the repository in square brackets; e.g., "[myrepo]".
+    the repository in square brackets; e.g., ``[myrepo]``.  Set this
+    value to the empty string to suppress the email prefix.
 
 multimailhook.emailMaxLines
 
@@ -299,7 +359,7 @@
 multimailhook.emailMaxLineLength
 
     The maximum length of a line in the email body.  Lines longer than
-    this limit are truncated to this length with a trailing " [...]"
+    this limit are truncated to this length with a trailing `` [...]``
     added to indicate the missing text.  The default is 500, because
     (a) diffs with longer lines are probably from binary files, for
     which a diff is useless, and (b) even if a text file has such long
@@ -316,54 +376,62 @@
 
 multimailhook.emailStrictUTF8
 
-    If this boolean option is set to "true", then the main part of the
+    If this boolean option is set to `true`, then the main part of the
     email body is forced to be valid UTF-8.  Any characters that are
     not valid UTF-8 are converted to the Unicode replacement
-    character, U+FFFD.  The default is "true".
+    character, U+FFFD.  The default is `true`.
 
 multimailhook.diffOpts
 
-    Options passed to "git diff-tree" when generating the summary
-    information for ReferenceChange emails.  Default is "--stat
-    --summary --find-copies-harder".  Add -p to those options to
+    Options passed to ``git diff-tree`` when generating the summary
+    information for ReferenceChange emails.  Default is ``--stat
+    --summary --find-copies-harder``.  Add -p to those options to
     include a unified diff of changes in addition to the usual summary
     output.  Shell quoting is allowed; see multimailhook.logOpts for
     details.
 
+multimailhook.graphOpts
+
+    Options passed to ``git log --graph`` when generating graphs for the
+    reference change summary emails (used only if refchangeShowGraph
+    is true).  The default is '--oneline --decorate'.
+
+    Shell quoting is allowed; see logOpts for details.
+
 multimailhook.logOpts
 
-    Options passed to "git log" to generate additional info for
+    Options passed to ``git log`` to generate additional info for
     reference change emails (used only if refchangeShowLog is set).
-    For example, adding --graph will show the graph of revisions, -p
-    will show the complete diff, etc.  The default is empty.
+    For example, adding -p will show each commit's complete diff.  The
+    default is empty.
 
     Shell quoting is allowed; for example, a log format that contains
-    spaces can be specified using something like:
+    spaces can be specified using something like::
 
       git config multimailhook.logopts '--pretty=format:"%h %aN <%aE>%n%s%n%n%b%n"'
 
     If you want to set this by editing your configuration file
     directly, remember that Git requires double-quotes to be escaped
-    (see git-config(1) for more information):
+    (see git-config(1) for more information)::
 
       [multimailhook]
               logopts = --pretty=format:\"%h %aN <%aE>%n%s%n%n%b%n\"
 
 multimailhook.commitLogOpts
 
-    Options passed to "git log" to generate additional info for
+    Options passed to ``git log`` to generate additional info for
     revision change emails.  For example, adding --ignore-all-spaces
-    will suppress whitespace changes.  The default options are "-C
-    --stat -p --cc".  Shell quoting is allowed; see
+    will suppress whitespace changes.  The default options are ``-C
+    --stat -p --cc``.  Shell quoting is allowed; see
     multimailhook.logOpts for details.
 
 multimailhook.emailDomain
 
     Domain name appended to the username of the person doing the push
-    to convert it into an email address (via "%s@%s" % (username,
-    emaildomain)).  More complicated schemes can be implemented by
-    overriding Environment and overriding its get_pusher_email()
-    method.
+    to convert it into an email address
+    (via ``"%s@%s" % (username, emaildomain)``). More complicated
+    schemes can be implemented by overriding Environment and
+    overriding its get_pusher_email() method.
 
 multimailhook.replyTo
 multimailhook.replyToCommit
@@ -377,26 +445,48 @@
 
     - An email address, which will be used directly.
 
-    - The value "pusher", in which case the pusher's address (if
+    - The value `pusher`, in which case the pusher's address (if
       available) will be used.  This is the default for refchange
       emails.
 
-    - The value "author" (meaningful only for replyToCommit), in which
+    - The value `author` (meaningful only for replyToCommit), in which
       case the commit author's address will be used.  This is the
       default for commit emails.
 
-    - The value "none", in which case the Reply-To: field will be
+    - The value `none`, in which case the Reply-To: field will be
       omitted.
 
+multimailhook.quiet
+
+    Do not output the list of email recipients from the hook
+
+multimailhook.stdout
+
+    For debugging, send emails to stdout rather than to the
+    mailer.  Equivalent to the --stdout command line option
+
+multimailhook.scanCommitForCc
+
+    If this option is set to true, than recipients from lines in commit body
+    that starts with ``CC:`` will be added to CC list.
+    Default: false
+
+multimailhook.combineWhenSingleCommit
+
+    If this option is set to true and a single new commit is pushed to
+    a branch, combine the summary and commit email messages into a
+    single email.
+    Default: true
+
 
 Email filtering aids
 --------------------
 
 All emails include extra headers to enable fine tuned filtering and
 give information for debugging.  All emails include the headers
-"X-Git-Host", "X-Git-Repo", "X-Git-Refname", and "X-Git-Reftype".
-ReferenceChange emails also include headers "X-Git-Oldrev" and "X-Git-Newrev";
-Revision emails also include header "X-Git-Rev".
+``X-Git-Host``, ``X-Git-Repo``, ``X-Git-Refname``, and ``X-Git-Reftype``.
+ReferenceChange emails also include headers ``X-Git-Oldrev`` and ``X-Git-Newrev``;
+Revision emails also include header ``X-Git-Rev``.
 
 
 Customizing email contents
@@ -420,16 +510,17 @@
 * GenericEnvironment: a stand-alone Git repository.
 
 * GitoliteEnvironment: a Git repository that is managed by gitolite
-  [3].  For such repositories, the identity of the pusher is read from
-  environment variable $GL_USER, and the name of the repository is
-  read from $GL_REPO (if it is not overridden by
-  multimailhook.reponame).
+  [3]_.  For such repositories, the identity of the pusher is read from
+  environment variable $GL_USER, the name of the repository is read
+  from $GL_REPO (if it is not overridden by multimailhook.reponame),
+  and the From: header value is optionally read from gitolite.conf
+  (see multimailhook.from).
 
 By default, git-multimail assumes GitoliteEnvironment if $GL_USER and
 $GL_REPO are set, and otherwise assumes GenericEnvironment.
 Alternatively, you can choose one of these two environments explicitly
-by setting a "multimailhook.environment" config setting (which can
-have the value "generic" or "gitolite") or by passing an --environment
+by setting a ``multimailhook.environment`` config setting (which can
+have the value `generic` or `gitolite`) or by passing an --environment
 option to the script.
 
 If you need to customize the script in ways that are not supported by
@@ -439,8 +530,8 @@
 post-receive script.  Then implement your environment class; it should
 usually inherit from one of the existing Environment classes and
 possibly one or more of the EnvironmentMixin classes.  Then set the
-"environment" variable to an instance of your own environment class
-and pass it to run_as_post_receive_hook().
+``environment`` variable to an instance of your own environment class
+and pass it to ``run_as_post_receive_hook()``.
 
 The standard environment classes, GenericEnvironment and
 GitoliteEnvironment, are in fact themselves put together out of a
@@ -490,12 +581,14 @@
 Footnotes
 ---------
 
-[1] http://www.python.org/dev/peps/pep-0394/
+.. [1] http://www.python.org/dev/peps/pep-0394/
 
-[2] Because of the way information is passed to update hooks, the
-    script's method of determining whether a commit has already been
-    seen does not work when it is used as an "update" script.  In
-    particular, no notification email will be generated for a new
-    commit that is added to multiple references in the same push.
+.. [2] Because of the way information is passed to update hooks, the
+       script's method of determining whether a commit has already
+       been seen does not work when it is used as an ``update`` script.
+       In particular, no notification email will be generated for a
+       new commit that is added to multiple references in the same
+       push. A workaround is to use --force-send to force sending the
+       emails.
 
-[3] https://github.com/sitaramc/gitolite
+.. [3] https://github.com/sitaramc/gitolite
diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git
index ab3ece5..f5d59a8 100644
--- a/contrib/hooks/multimail/README.Git
+++ b/contrib/hooks/multimail/README.Git
@@ -6,10 +6,10 @@
     https://github.com/git-multimail/git-multimail
 
 The version in this directory was obtained from the upstream project
-on 2015-04-27 and consists of the "git-multimail" subdirectory from
+on July 03 2015 and consists of the "git-multimail" subdirectory from
 revision
 
-    8c3aaafa873bf10de8dddf1d202c449b3eff3b42 refs/tags/1.0.2
+    6d6c9eb62a054143322cfaecde3949189c065b46 refs/tags/1.1.1
 
 Please see the README file in this directory for information about how
 to report bugs or contribute to git-multimail.
diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py
index 8b58ed6..c06ce7a 100755
--- a/contrib/hooks/multimail/git_multimail.py
+++ b/contrib/hooks/multimail/git_multimail.py
@@ -1,5 +1,6 @@
 #! /usr/bin/env python2
 
+# Copyright (c) 2015 Matthieu Moy and others
 # Copyright (c) 2012-2014 Michael Haggerty and others
 # Derived from contrib/hooks/post-receive-email, which is
 # Copyright (c) 2007 Andy Parkins
@@ -99,6 +100,10 @@
     ' (was %(oldrev_short)s)'
     )
 
+COMBINED_REFCHANGE_REVISION_SUBJECT_TEMPLATE = (
+    '%(emailprefix)s%(refname_type)s %(short_refname)s updated: %(oneline)s'
+    )
+
 REFCHANGE_HEADER_TEMPLATE = """\
 Date: %(send_date)s
 To: %(recipients)s
@@ -230,6 +235,7 @@
 REVISION_HEADER_TEMPLATE = """\
 Date: %(send_date)s
 To: %(recipients)s
+Cc: %(cc_recipients)s
 Subject: %(emailprefix)s%(num)02d/%(tot)02d: %(oneline)s
 MIME-Version: 1.0
 Content-Type: text/plain; charset=%(charset)s
@@ -258,6 +264,38 @@
 REVISION_FOOTER_TEMPLATE = FOOTER_TEMPLATE
 
 
+# Combined, meaning refchange+revision email (for single-commit additions)
+COMBINED_HEADER_TEMPLATE = """\
+Date: %(send_date)s
+To: %(recipients)s
+Subject: %(subject)s
+MIME-Version: 1.0
+Content-Type: text/plain; charset=%(charset)s
+Content-Transfer-Encoding: 8bit
+Message-ID: %(msgid)s
+From: %(fromaddr)s
+Reply-To: %(reply_to)s
+X-Git-Host: %(fqdn)s
+X-Git-Repo: %(repo_shortname)s
+X-Git-Refname: %(refname)s
+X-Git-Reftype: %(refname_type)s
+X-Git-Oldrev: %(oldrev)s
+X-Git-Newrev: %(newrev)s
+X-Git-Rev: %(rev)s
+Auto-Submitted: auto-generated
+"""
+
+COMBINED_INTRO_TEMPLATE = """\
+This is an automated email from the git hooks/post-receive script.
+
+%(pusher)s pushed a commit to %(refname_type)s %(short_refname)s
+in repository %(repo_shortname)s.
+
+"""
+
+COMBINED_FOOTER_TEMPLATE = FOOTER_TEMPLATE
+
+
 class CommandError(Exception):
     def __init__(self, cmd, retcode):
         self.cmd = cmd
@@ -336,6 +374,47 @@
     return read_git_output(args, keepends=True, **kw).splitlines(keepends)
 
 
+def git_rev_list_ish(cmd, spec, args=None, **kw):
+    """Common functionality for invoking a 'git rev-list'-like command.
+
+    Parameters:
+      * cmd is the Git command to run, e.g., 'rev-list' or 'log'.
+      * spec is a list of revision arguments to pass to the named
+        command.  If None, this function returns an empty list.
+      * args is a list of extra arguments passed to the named command.
+      * All other keyword arguments (if any) are passed to the
+        underlying read_git_lines() function.
+
+    Return the output of the Git command in the form of a list, one
+    entry per output line.
+    """
+    if spec is None:
+        return []
+    if args is None:
+        args = []
+    args = [cmd, '--stdin'] + args
+    spec_stdin = ''.join(s + '\n' for s in spec)
+    return read_git_lines(args, input=spec_stdin, **kw)
+
+
+def git_rev_list(spec, **kw):
+    """Run 'git rev-list' with the given list of revision arguments.
+
+    See git_rev_list_ish() for parameter and return value
+    documentation.
+    """
+    return git_rev_list_ish('rev-list', spec, **kw)
+
+
+def git_log(spec, **kw):
+    """Run 'git log' with the given list of revision arguments.
+
+    See git_rev_list_ish() for parameter and return value
+    documentation.
+    """
+    return git_rev_list_ish('log', spec, **kw)
+
+
 def header_encode(text, header_name=None):
     """Encode and line-wrap the value of an email header field."""
 
@@ -388,9 +467,9 @@
     def get(self, name, default=None):
         try:
             values = self._split(read_git_output(
-                    ['config', '--get', '--null', '%s.%s' % (self.section, name)],
-                    env=self.env, keepends=True,
-                    ))
+                ['config', '--get', '--null', '%s.%s' % (self.section, name)],
+                env=self.env, keepends=True,
+                ))
             assert len(values) == 1
             return values[0]
         except CommandError:
@@ -449,9 +528,14 @@
             env=self.env,
             )
 
-    def has_key(self, name):
+    def __contains__(self, name):
         return self.get_all(name, default=None) is not None
 
+    # We don't use this method anymore internally, but keep it here in
+    # case somebody is calling it from their own code:
+    def has_key(self, name):
+        return name in self
+
     def unset_all(self, name):
         try:
             read_git_output(
@@ -579,7 +663,7 @@
         self._values = None
 
     def _compute_values(self):
-        """Return a dictionary {keyword : expansion} for this Change.
+        """Return a dictionary {keyword: expansion} for this Change.
 
         Derived classes overload this method to add more entries to
         the return value.  This method is used internally by
@@ -589,7 +673,7 @@
         return self.environment.get_values()
 
     def get_values(self, **extra_values):
-        """Return a dictionary {keyword : expansion} for this Change.
+        """Return a dictionary {keyword: expansion} for this Change.
 
         Return a dictionary mapping keywords to the values that they
         should be expanded to for this Change (used when interpolating
@@ -636,7 +720,7 @@
                 value = value % values
             except KeyError, e:
                 if DEBUG:
-                    sys.stderr.write(
+                    self.environment.log_warning(
                         'Warning: unknown variable %r in the following line; line skipped:\n'
                         '    %s\n'
                         % (e.args[0], line,)
@@ -711,6 +795,8 @@
 class Revision(Change):
     """A Change consisting of a single git commit."""
 
+    CC_RE = re.compile(r'^\s*C[Cc]:\s*(?P<to>[^#]+@[^\s#]*)\s*(#.*)?$')
+
     def __init__(self, reference_change, rev, num, tot):
         Change.__init__(self, reference_change.environment)
         self.reference_change = reference_change
@@ -722,6 +808,24 @@
         self.author = read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1])
         self.recipients = self.environment.get_revision_recipients(self)
 
+        self.cc_recipients = ''
+        if self.environment.get_scancommitforcc():
+            self.cc_recipients = ', '.join(to.strip() for to in self._cc_recipients())
+            if self.cc_recipients:
+                self.environment.log_msg(
+                    'Add %s to CC for %s\n' % (self.cc_recipients, self.rev.sha1))
+
+    def _cc_recipients(self):
+        cc_recipients = []
+        message = read_git_output(['log', '--no-walk', '--format=%b', self.rev.sha1])
+        lines = message.strip().split('\n')
+        for line in lines:
+            m = re.match(self.CC_RE, line)
+            if m:
+                cc_recipients.append(m.group('to'))
+
+        return cc_recipients
+
     def _compute_values(self):
         values = Change._compute_values(self)
 
@@ -739,6 +843,8 @@
         values['num'] = self.num
         values['tot'] = self.tot
         values['recipients'] = self.recipients
+        if self.cc_recipients:
+            values['cc_recipients'] = self.cc_recipients
         values['oneline'] = oneline
         values['author'] = self.author
 
@@ -750,8 +856,8 @@
 
     def generate_email_header(self, **extra_values):
         for line in self.expand_header_lines(
-            REVISION_HEADER_TEMPLATE, **extra_values
-            ):
+                REVISION_HEADER_TEMPLATE, **extra_values
+                ):
             yield line
 
     def generate_email_intro(self):
@@ -822,26 +928,26 @@
                 klass = BranchChange
             elif area == 'remotes':
                 # Tracking branch:
-                sys.stderr.write(
+                environment.log_warning(
                     '*** Push-update of tracking branch %r\n'
                     '***  - incomplete email generated.\n'
-                     % (refname,)
+                    % (refname,)
                     )
                 klass = OtherReferenceChange
             else:
                 # Some other reference namespace:
-                sys.stderr.write(
+                environment.log_warning(
                     '*** Push-update of strange reference %r\n'
                     '***  - incomplete email generated.\n'
-                     % (refname,)
+                    % (refname,)
                     )
                 klass = OtherReferenceChange
         else:
             # Anything else (is there anything else?)
-            sys.stderr.write(
+            environment.log_warning(
                 '*** Unknown type of update to %r (%s)\n'
                 '***  - incomplete email generated.\n'
-                 % (refname, rev.type,)
+                % (refname, rev.type,)
                 )
             klass = OtherReferenceChange
 
@@ -854,9 +960,9 @@
     def __init__(self, environment, refname, short_refname, old, new, rev):
         Change.__init__(self, environment)
         self.change_type = {
-            (False, True) : 'create',
-            (True, True) : 'update',
-            (True, False) : 'delete',
+            (False, True): 'create',
+            (True, True): 'update',
+            (True, False): 'delete',
             }[bool(old), bool(new)]
         self.refname = refname
         self.short_refname = short_refname
@@ -865,10 +971,16 @@
         self.rev = rev
         self.msgid = make_msgid()
         self.diffopts = environment.diffopts
+        self.graphopts = environment.graphopts
         self.logopts = environment.logopts
         self.commitlogopts = environment.commitlogopts
+        self.showgraph = environment.refchange_showgraph
         self.showlog = environment.refchange_showlog
 
+        self.header_template = REFCHANGE_HEADER_TEMPLATE
+        self.intro_template = REFCHANGE_INTRO_TEMPLATE
+        self.footer_template = FOOTER_TEMPLATE
+
     def _compute_values(self):
         values = Change._compute_values(self)
 
@@ -894,11 +1006,39 @@
 
         return values
 
+    def send_single_combined_email(self, known_added_sha1s):
+        """Determine if a combined refchange/revision email should be sent
+
+        If there is only a single new (non-merge) commit added by a
+        change, it is useful to combine the ReferenceChange and
+        Revision emails into one.  In such a case, return the single
+        revision; otherwise, return None.
+
+        This method is overridden in BranchChange."""
+
+        return None
+
+    def generate_combined_email(self, push, revision, body_filter=None, extra_header_values={}):
+        """Generate an email describing this change AND specified revision.
+
+        Iterate over the lines (including the header lines) of an
+        email describing this change.  If body_filter is not None,
+        then use it to filter the lines that are intended for the
+        email body.
+
+        The extra_header_values field is received as a dict and not as
+        **kwargs, to allow passing other keyword arguments in the
+        future (e.g. passing extra values to generate_email_intro()
+
+        This method is overridden in BranchChange."""
+
+        raise NotImplementedError
+
     def get_subject(self):
         template = {
-            'create' : REF_CREATED_SUBJECT_TEMPLATE,
-            'update' : REF_UPDATED_SUBJECT_TEMPLATE,
-            'delete' : REF_DELETED_SUBJECT_TEMPLATE,
+            'create': REF_CREATED_SUBJECT_TEMPLATE,
+            'update': REF_UPDATED_SUBJECT_TEMPLATE,
+            'delete': REF_DELETED_SUBJECT_TEMPLATE,
             }[self.change_type]
         return self.expand(template)
 
@@ -907,12 +1047,12 @@
             extra_values['subject'] = self.get_subject()
 
         for line in self.expand_header_lines(
-            REFCHANGE_HEADER_TEMPLATE, **extra_values
-            ):
+                self.header_template, **extra_values
+                ):
             yield line
 
     def generate_email_intro(self):
-        for line in self.expand_lines(REFCHANGE_INTRO_TEMPLATE):
+        for line in self.expand_lines(self.intro_template):
             yield line
 
     def generate_email_body(self, push):
@@ -922,9 +1062,9 @@
         generate_update_summary() / generate_delete_summary()."""
 
         change_summary = {
-            'create' : self.generate_create_summary,
-            'delete' : self.generate_delete_summary,
-            'update' : self.generate_update_summary,
+            'create': self.generate_create_summary,
+            'delete': self.generate_delete_summary,
+            'update': self.generate_update_summary,
             }[self.change_type](push)
         for line in change_summary:
             yield line
@@ -933,7 +1073,23 @@
             yield line
 
     def generate_email_footer(self):
-        return self.expand_lines(FOOTER_TEMPLATE)
+        return self.expand_lines(self.footer_template)
+
+    def generate_revision_change_graph(self, push):
+        if self.showgraph:
+            args = ['--graph'] + self.graphopts
+            for newold in ('new', 'old'):
+                has_newold = False
+                spec = push.get_commits_spec(newold, self)
+                for line in git_log(spec, args=args, keepends=True):
+                    if not has_newold:
+                        has_newold = True
+                        yield '\n'
+                        yield 'Graph of %s commits:\n\n' % (
+                            {'new': 'new', 'old': 'discarded'}[newold],)
+                    yield '  ' + line
+                if has_newold:
+                    yield '\n'
 
     def generate_revision_change_log(self, new_commits_list):
         if self.showlog:
@@ -945,9 +1101,17 @@
                     + new_commits_list
                     + ['--'],
                     keepends=True,
-                ):
+                    ):
                 yield line
 
+    def generate_new_revision_summary(self, tot, new_commits_list, push):
+        for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=tot):
+            yield line
+        for line in self.generate_revision_change_graph(push):
+            yield line
+        for line in self.generate_revision_change_log(new_commits_list):
+            yield line
+
     def generate_revision_change_summary(self, push):
         """Generate a summary of the revisions added/removed by this change."""
 
@@ -960,7 +1124,7 @@
             sha1s.reverse()
             tot = len(sha1s)
             new_revisions = [
-                Revision(self, GitObject(sha1), num=i+1, tot=tot)
+                Revision(self, GitObject(sha1), num=i + 1, tot=tot)
                 for (i, sha1) in enumerate(sha1s)
                 ]
 
@@ -973,9 +1137,8 @@
                         BRIEF_SUMMARY_TEMPLATE, action='new', text=subject,
                         )
                 yield '\n'
-                for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=tot):
-                    yield line
-                for line in self.generate_revision_change_log([r.rev.sha1 for r in new_revisions]):
+                for line in self.generate_new_revision_summary(
+                        tot, [r.rev.sha1 for r in new_revisions], push):
                     yield line
             else:
                 for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE):
@@ -993,16 +1156,16 @@
             # revisions in the summary even though we will not send
             # new notification emails for them.
             adds = list(generate_summaries(
-                    '--topo-order', '--reverse', '%s..%s'
-                    % (self.old.commit_sha1, self.new.commit_sha1,)
-                    ))
+                '--topo-order', '--reverse', '%s..%s'
+                % (self.old.commit_sha1, self.new.commit_sha1,)
+                ))
 
             # List of the revisions that were removed from the branch
             # by this update.  This will be empty except for
             # non-fast-forward updates.
             discards = list(generate_summaries(
-                    '%s..%s' % (self.new.commit_sha1, self.old.commit_sha1,)
-                    ))
+                '%s..%s' % (self.new.commit_sha1, self.old.commit_sha1,)
+                ))
 
             if adds:
                 new_commits_list = push.get_new_commits(self)
@@ -1071,13 +1234,14 @@
             yield '\n'
 
             if new_commits:
-                for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=len(new_commits)):
-                    yield line
-                for line in self.generate_revision_change_log(new_commits_list):
+                for line in self.generate_new_revision_summary(
+                        len(new_commits), new_commits_list, push):
                     yield line
             else:
                 for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE):
                     yield line
+                for line in self.generate_revision_change_graph(push):
+                    yield line
 
             # The diffstat is shown from the old revision to the new
             # revision.  This is to show the truth of what happened in
@@ -1089,11 +1253,11 @@
             yield '\n'
             yield 'Summary of changes:\n'
             for line in read_git_lines(
-                ['diff-tree']
-                + self.diffopts
-                + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)],
-                keepends=True,
-                ):
+                    ['diff-tree']
+                    + self.diffopts
+                    + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)],
+                    keepends=True,
+                    ):
                 yield line
 
         elif self.old.commit_sha1 and not self.new.commit_sha1:
@@ -1103,7 +1267,7 @@
             sha1s = list(push.get_discarded_commits(self))
             tot = len(sha1s)
             discarded_revisions = [
-                Revision(self, GitObject(sha1), num=i+1, tot=tot)
+                Revision(self, GitObject(sha1), num=i + 1, tot=tot)
                 for (i, sha1) in enumerate(sha1s)
                 ]
 
@@ -1116,6 +1280,8 @@
                     yield r.expand(
                         BRIEF_SUMMARY_TEMPLATE, action='discards', text=subject,
                         )
+                for line in self.generate_revision_change_graph(push):
+                    yield line
             else:
                 for line in self.expand_lines(NO_DISCARDED_REVISIONS_TEMPLATE):
                     yield line
@@ -1161,6 +1327,150 @@
             old=old, new=new, rev=rev,
             )
         self.recipients = environment.get_refchange_recipients(self)
+        self._single_revision = None
+
+    def send_single_combined_email(self, known_added_sha1s):
+        if not self.environment.combine_when_single_commit:
+            return None
+
+        # In the sadly-all-too-frequent usecase of people pushing only
+        # one of their commits at a time to a repository, users feel
+        # the reference change summary emails are noise rather than
+        # important signal.  This is because, in this particular
+        # usecase, there is a reference change summary email for each
+        # new commit, and all these summaries do is point out that
+        # there is one new commit (which can readily be inferred by
+        # the existence of the individual revision email that is also
+        # sent).  In such cases, our users prefer there to be a combined
+        # reference change summary/new revision email.
+        #
+        # So, if the change is an update and it doesn't discard any
+        # commits, and it adds exactly one non-merge commit (gerrit
+        # forces a workflow where every commit is individually merged
+        # and the git-multimail hook fired off for just this one
+        # change), then we send a combined refchange/revision email.
+        try:
+            # If this change is a reference update that doesn't discard
+            # any commits...
+            if self.change_type != 'update':
+                return None
+
+            if read_git_lines(
+                    ['merge-base', self.old.sha1, self.new.sha1]
+                    ) != [self.old.sha1]:
+                return None
+
+            # Check if this update introduced exactly one non-merge
+            # commit:
+
+            def split_line(line):
+                """Split line into (sha1, [parent,...])."""
+
+                words = line.split()
+                return (words[0], words[1:])
+
+            # Get the new commits introduced by the push as a list of
+            # (sha1, [parent,...])
+            new_commits = [
+                split_line(line)
+                for line in read_git_lines(
+                    [
+                        'log', '-3', '--format=%H %P',
+                        '%s..%s' % (self.old.sha1, self.new.sha1),
+                        ]
+                    )
+                ]
+
+            if not new_commits:
+                return None
+
+            # If the newest commit is a merge, save it for a later check
+            # but otherwise ignore it
+            merge = None
+            tot = len(new_commits)
+            if len(new_commits[0][1]) > 1:
+                merge = new_commits[0][0]
+                del new_commits[0]
+
+            # Our primary check: we can't combine if more than one commit
+            # is introduced.  We also currently only combine if the new
+            # commit is a non-merge commit, though it may make sense to
+            # combine if it is a merge as well.
+            if not (
+                    len(new_commits) == 1
+                    and len(new_commits[0][1]) == 1
+                    and new_commits[0][0] in known_added_sha1s
+                    ):
+                return None
+
+            # We do not want to combine revision and refchange emails if
+            # those go to separate locations.
+            rev = Revision(self, GitObject(new_commits[0][0]), 1, tot)
+            if rev.recipients != self.recipients:
+                return None
+
+            # We ignored the newest commit if it was just a merge of the one
+            # commit being introduced.  But we don't want to ignore that
+            # merge commit it it involved conflict resolutions.  Check that.
+            if merge and merge != read_git_output(['diff-tree', '--cc', merge]):
+                return None
+
+            # We can combine the refchange and one new revision emails
+            # into one.  Return the Revision that a combined email should
+            # be sent about.
+            return rev
+        except CommandError:
+            # Cannot determine number of commits in old..new or new..old;
+            # don't combine reference/revision emails:
+            return None
+
+    def generate_combined_email(self, push, revision, body_filter=None, extra_header_values={}):
+        values = revision.get_values()
+        if extra_header_values:
+            values.update(extra_header_values)
+        if 'subject' not in extra_header_values:
+            values['subject'] = self.expand(COMBINED_REFCHANGE_REVISION_SUBJECT_TEMPLATE, **values)
+
+        self._single_revision = revision
+        self.header_template = COMBINED_HEADER_TEMPLATE
+        self.intro_template = COMBINED_INTRO_TEMPLATE
+        self.footer_template = COMBINED_FOOTER_TEMPLATE
+        for line in self.generate_email(push, body_filter, values):
+            yield line
+
+    def generate_email_body(self, push):
+        '''Call the appropriate body generation routine.
+
+        If this is a combined refchange/revision email, the special logic
+        for handling this combined email comes from this function.  For
+        other cases, we just use the normal handling.'''
+
+        # If self._single_revision isn't set; don't override
+        if not self._single_revision:
+            for line in super(BranchChange, self).generate_email_body(push):
+                yield line
+            return
+
+        # This is a combined refchange/revision email; we first provide
+        # some info from the refchange portion, and then call the revision
+        # generate_email_body function to handle the revision portion.
+        adds = list(generate_summaries(
+            '--topo-order', '--reverse', '%s..%s'
+            % (self.old.commit_sha1, self.new.commit_sha1,)
+            ))
+
+        yield self.expand("The following commit(s) were added to %(refname)s by this push:\n")
+        for (sha1, subject) in adds:
+            yield self.expand(
+                BRIEF_SUMMARY_TEMPLATE, action='new',
+                rev_short=sha1, text=subject,
+                )
+
+        yield self._single_revision.rev.short + " is described below\n"
+        yield '\n'
+
+        for line in self._single_revision.generate_email_body(push):
+            yield line
 
 
 class AnnotatedTagChange(ReferenceChange):
@@ -1390,13 +1700,17 @@
             sys.exit(1)
         try:
             p.stdin.writelines(lines)
-        except:
+        except Exception, e:
             sys.stderr.write(
                 '*** Error while generating commit email\n'
                 '***  - mail sending aborted.\n'
                 )
-            p.terminate()
-            raise
+            try:
+                # subprocess.terminate() is not available in Python 2.4
+                p.terminate()
+            except AttributeError:
+                pass
+            raise e
         else:
             p.stdin.close()
             retcode = p.wait()
@@ -1407,34 +1721,78 @@
 class SMTPMailer(Mailer):
     """Send emails using Python's smtplib."""
 
-    def __init__(self, envelopesender, smtpserver):
+    def __init__(self, envelopesender, smtpserver,
+                 smtpservertimeout=10.0, smtpserverdebuglevel=0,
+                 smtpencryption='none',
+                 smtpuser='', smtppass='',
+                 ):
         if not envelopesender:
             sys.stderr.write(
                 'fatal: git_multimail: cannot use SMTPMailer without a sender address.\n'
                 'please set either multimailhook.envelopeSender or user.email\n'
                 )
             sys.exit(1)
+        if smtpencryption == 'ssl' and not (smtpuser and smtppass):
+            raise ConfigurationException(
+                'Cannot use SMTPMailer with security option ssl '
+                'without options username and password.'
+                )
         self.envelopesender = envelopesender
         self.smtpserver = smtpserver
+        self.smtpservertimeout = smtpservertimeout
+        self.smtpserverdebuglevel = smtpserverdebuglevel
+        self.security = smtpencryption
+        self.username = smtpuser
+        self.password = smtppass
         try:
-            self.smtp = smtplib.SMTP(self.smtpserver)
+            def call(klass, server, timeout):
+                try:
+                    return klass(server, timeout=timeout)
+                except TypeError:
+                    # Old Python versions do not have timeout= argument.
+                    return klass(server)
+            if self.security == 'none':
+                self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout)
+            elif self.security == 'ssl':
+                self.smtp = call(smtplib.SMTP_SSL, self.smtpserver, timeout=self.smtpservertimeout)
+            elif self.security == 'tls':
+                if ':' not in self.smtpserver:
+                    self.smtpserver += ':587'  # default port for TLS
+                self.smtp = call(smtplib.SMTP, self.smtpserver, timeout=self.smtpservertimeout)
+                self.smtp.ehlo()
+                self.smtp.starttls()
+                self.smtp.ehlo()
+            else:
+                sys.stdout.write('*** Error: Control reached an invalid option. ***')
+                sys.exit(1)
+            if self.smtpserverdebuglevel > 0:
+                sys.stdout.write(
+                    "*** Setting debug on for SMTP server connection (%s) ***\n"
+                    % self.smtpserverdebuglevel)
+                self.smtp.set_debuglevel(self.smtpserverdebuglevel)
         except Exception, e:
-            sys.stderr.write('*** Error establishing SMTP connection to %s***\n' % self.smtpserver)
+            sys.stderr.write(
+                '*** Error establishing SMTP connection to %s ***\n'
+                % self.smtpserver)
             sys.stderr.write('*** %s\n' % str(e))
             sys.exit(1)
 
     def __del__(self):
-        self.smtp.quit()
+        if hasattr(self, 'smtp'):
+            self.smtp.quit()
 
     def send(self, lines, to_addrs):
         try:
+            if self.username or self.password:
+                sys.stderr.write("*** Authenticating as %s ***\n" % self.username)
+                self.smtp.login(self.username, self.password)
             msg = ''.join(lines)
             # turn comma-separated list into Python list if needed.
             if isinstance(to_addrs, basestring):
                 to_addrs = [email for (name, email) in getaddresses([to_addrs])]
             self.smtp.sendmail(self.envelopesender, to_addrs, msg)
         except Exception, e:
-            sys.stderr.write('*** Error sending email***\n')
+            sys.stderr.write('*** Error sending email ***\n')
             sys.stderr.write('*** %s\n' % str(e))
             self.smtp.quit()
             sys.exit(1)
@@ -1549,6 +1907,10 @@
 
             True iff announce emails should include a shortlog.
 
+        refchange_showgraph (bool)
+
+            True iff refchanges emails should include a detailed graph.
+
         refchange_showlog (bool)
 
             True iff refchanges emails should include a detailed log.
@@ -1559,6 +1921,12 @@
             summary email.  The value should be a list of strings
             representing words to be passed to the command.
 
+        graphopts (list of strings)
+
+            Analogous to diffopts, but contains options passed to
+            'git log --graph' when generating the detailed graph for
+            a set of commits (see refchange_showgraph)
+
         logopts (list of strings)
 
             Analogous to diffopts, but contains options passed to
@@ -1571,6 +1939,17 @@
             commit mail.  The value should be a list of strings
             representing words to be passed to the command.
 
+        quiet (bool)
+            On success do not write to stderr
+
+        stdout (bool)
+            Write email to stdout rather than emailing. Useful for debugging
+
+        combine_when_single_commit (bool)
+
+            True if a combined email should be produced when a single
+            new commit is pushed to a branch, False otherwise.
+
     """
 
     REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\.git)$')
@@ -1580,9 +1959,14 @@
         self.announce_show_shortlog = False
         self.maxcommitemails = 500
         self.diffopts = ['--stat', '--summary', '--find-copies-harder']
+        self.graphopts = ['--oneline', '--decorate']
         self.logopts = []
+        self.refchange_showgraph = False
         self.refchange_showlog = False
         self.commitlogopts = ['-C', '--stat', '-p', '--cc']
+        self.quiet = False
+        self.stdout = False
+        self.combine_when_single_commit = True
 
         self.COMPUTED_KEYS = [
             'administrator',
@@ -1614,6 +1998,14 @@
     def get_pusher_email(self):
         return None
 
+    def get_fromaddr(self):
+        config = Config('user')
+        fromname = config.get('name', default='')
+        fromemail = config.get('email', default='')
+        if fromemail:
+            return formataddr([fromname, fromemail])
+        return self.get_sender()
+
     def get_administrator(self):
         return 'the administrator of this repository'
 
@@ -1631,7 +2023,7 @@
         return CHARSET
 
     def get_values(self):
-        """Return a dictionary {keyword : expansion} for this Environment.
+        """Return a dictionary {keyword: expansion} for this Environment.
 
         This method is called by Change._compute_values().  The keys
         in the returned dictionary are available to be used in any of
@@ -1699,6 +2091,24 @@
 
         return lines
 
+    def log_msg(self, msg):
+        """Write the string msg on a log file or on stderr.
+
+        Sends the text to stderr by default, override to change the behavior."""
+        sys.stderr.write(msg)
+
+    def log_warning(self, msg):
+        """Write the string msg on a log file or on stderr.
+
+        Sends the text to stderr by default, override to change the behavior."""
+        sys.stderr.write(msg)
+
+    def log_error(self, msg):
+        """Write the string msg on a log file or on stderr.
+
+        Sends the text to stderr by default, override to change the behavior."""
+        sys.stderr.write(msg)
+
 
 class ConfigEnvironmentMixin(Environment):
     """A mixin that sets self.config to its constructor's config argument.
@@ -1723,20 +2133,23 @@
             config=config, **kw
             )
 
-        self.announce_show_shortlog = config.get_bool(
-            'announceshortlog', default=self.announce_show_shortlog
-            )
-
-        self.refchange_showlog = config.get_bool(
-            'refchangeshowlog', default=self.refchange_showlog
-            )
+        for var, cfg in (
+                ('announce_show_shortlog', 'announceshortlog'),
+                ('refchange_showgraph', 'refchangeShowGraph'),
+                ('refchange_showlog', 'refchangeshowlog'),
+                ('quiet', 'quiet'),
+                ('stdout', 'stdout'),
+                ):
+            val = config.get_bool(cfg)
+            if val is not None:
+                setattr(self, var, val)
 
         maxcommitemails = config.get('maxcommitemails')
         if maxcommitemails is not None:
             try:
                 self.maxcommitemails = int(maxcommitemails)
             except ValueError:
-                sys.stderr.write(
+                self.log_warning(
                     '*** Malformed value for multimailhook.maxCommitEmails: %s\n' % maxcommitemails
                     + '*** Expected a number.  Ignoring.\n'
                     )
@@ -1745,6 +2158,10 @@
         if diffopts is not None:
             self.diffopts = shlex.split(diffopts)
 
+        graphopts = config.get('graphOpts')
+        if graphopts is not None:
+            self.graphopts = shlex.split(graphopts)
+
         logopts = config.get('logopts')
         if logopts is not None:
             self.logopts = shlex.split(logopts)
@@ -1756,14 +2173,18 @@
         reply_to = config.get('replyTo')
         self.__reply_to_refchange = config.get('replyToRefchange', default=reply_to)
         if (
-            self.__reply_to_refchange is not None
-            and self.__reply_to_refchange.lower() == 'author'
-            ):
+                self.__reply_to_refchange is not None
+                and self.__reply_to_refchange.lower() == 'author'
+                ):
             raise ConfigurationException(
                 '"author" is not an allowed setting for replyToRefchange'
                 )
         self.__reply_to_commit = config.get('replyToCommit', default=reply_to)
 
+        combine = config.get_bool('combineWhenSingleCommit')
+        if combine is not None:
+            self.combine_when_single_commit = combine
+
     def get_administrator(self):
         return (
             self.config.get('administrator')
@@ -1779,8 +2200,12 @@
 
     def get_emailprefix(self):
         emailprefix = self.config.get('emailprefix')
-        if emailprefix and emailprefix.strip():
-            return emailprefix.strip() + ' '
+        if emailprefix is not None:
+            emailprefix = emailprefix.strip()
+            if emailprefix:
+                return emailprefix + ' '
+            else:
+                return ''
         else:
             return '[%s] ' % (self.get_repo_shortname(),)
 
@@ -1791,14 +2216,7 @@
         fromaddr = self.config.get('from')
         if fromaddr:
             return fromaddr
-        else:
-            config = Config('user')
-            fromname = config.get('name', default='')
-            fromemail = config.get('email', default='')
-            if fromemail:
-                return formataddr([fromname, fromemail])
-            else:
-                return self.get_sender()
+        return super(ConfigOptionsEnvironmentMixin, self).get_fromaddr()
 
     def get_reply_to_refchange(self, refchange):
         if self.__reply_to_refchange is None:
@@ -1814,7 +2232,7 @@
         if self.__reply_to_commit is None:
             return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_commit(revision)
         elif self.__reply_to_commit.lower() == 'author':
-            return revision.get_author()
+            return revision.author
         elif self.__reply_to_commit.lower() == 'pusher':
             return self.get_pusher_email()
         elif self.__reply_to_commit.lower() == 'none':
@@ -1822,6 +2240,9 @@
         else:
             return self.__reply_to_commit
 
+    def get_scancommitforcc(self):
+        return self.config.get('scancommitforcc')
+
 
 class FilterLinesEnvironmentMixin(Environment):
     """Handle encoding and maximum line length of body lines.
@@ -1862,9 +2283,9 @@
 
 
 class ConfigFilterLinesEnvironmentMixin(
-    ConfigEnvironmentMixin,
-    FilterLinesEnvironmentMixin,
-    ):
+        ConfigEnvironmentMixin,
+        FilterLinesEnvironmentMixin,
+        ):
     """Handle encoding and maximum line length based on config."""
 
     def __init__(self, config, **kw):
@@ -1896,9 +2317,9 @@
 
 
 class ConfigMaxlinesEnvironmentMixin(
-    ConfigEnvironmentMixin,
-    MaxlinesEnvironmentMixin,
-    ):
+        ConfigEnvironmentMixin,
+        MaxlinesEnvironmentMixin,
+        ):
     """Limit the email body to the number of lines specified in config."""
 
     def __init__(self, config, **kw):
@@ -1927,9 +2348,9 @@
 
 
 class ConfigFQDNEnvironmentMixin(
-    ConfigEnvironmentMixin,
-    FQDNEnvironmentMixin,
-    ):
+        ConfigEnvironmentMixin,
+        FQDNEnvironmentMixin,
+        ):
     """Read the FQDN from the config."""
 
     def __init__(self, config, **kw):
@@ -1970,10 +2391,10 @@
     """Set recipients statically based on constructor parameters."""
 
     def __init__(
-        self,
-        refchange_recipients, announce_recipients, revision_recipients,
-        **kw
-        ):
+            self,
+            refchange_recipients, announce_recipients, revision_recipients, scancommitforcc,
+            **kw
+            ):
         super(StaticRecipientsEnvironmentMixin, self).__init__(**kw)
 
         # The recipients for various types of notification emails, as
@@ -1985,7 +2406,8 @@
         # compute them once and for all:
         if not (refchange_recipients
                 or announce_recipients
-                or revision_recipients):
+                or revision_recipients
+                or scancommitforcc):
             raise ConfigurationException('No email recipients configured!')
         self.__refchange_recipients = refchange_recipients
         self.__announce_recipients = announce_recipients
@@ -2002,9 +2424,9 @@
 
 
 class ConfigRecipientsEnvironmentMixin(
-    ConfigEnvironmentMixin,
-    StaticRecipientsEnvironmentMixin
-    ):
+        ConfigEnvironmentMixin,
+        StaticRecipientsEnvironmentMixin
+        ):
     """Determine recipients statically based on config."""
 
     def __init__(self, config, **kw):
@@ -2019,6 +2441,7 @@
             revision_recipients=self._get_recipients(
                 config, 'commitlist', 'mailinglist',
                 ),
+            scancommitforcc=config.get('scancommitforcc'),
             **kw
             )
 
@@ -2067,20 +2490,20 @@
 
 class GenericEnvironmentMixin(Environment):
     def get_pusher(self):
-        return self.osenv.get('USER', 'unknown user')
+        return self.osenv.get('USER', self.osenv.get('USERNAME', 'unknown user'))
 
 
 class GenericEnvironment(
-    ProjectdescEnvironmentMixin,
-    ConfigMaxlinesEnvironmentMixin,
-    ComputeFQDNEnvironmentMixin,
-    ConfigFilterLinesEnvironmentMixin,
-    ConfigRecipientsEnvironmentMixin,
-    PusherDomainEnvironmentMixin,
-    ConfigOptionsEnvironmentMixin,
-    GenericEnvironmentMixin,
-    Environment,
-    ):
+        ProjectdescEnvironmentMixin,
+        ConfigMaxlinesEnvironmentMixin,
+        ComputeFQDNEnvironmentMixin,
+        ConfigFilterLinesEnvironmentMixin,
+        ConfigRecipientsEnvironmentMixin,
+        PusherDomainEnvironmentMixin,
+        ConfigOptionsEnvironmentMixin,
+        GenericEnvironmentMixin,
+        Environment,
+        ):
     pass
 
 
@@ -2097,6 +2520,45 @@
     def get_pusher(self):
         return self.osenv.get('GL_USER', 'unknown user')
 
+    def get_fromaddr(self):
+        GL_USER = self.osenv.get('GL_USER')
+        if GL_USER is not None:
+            # Find the path to gitolite.conf.  Note that gitolite v3
+            # did away with the GL_ADMINDIR and GL_CONF environment
+            # variables (they are now hard-coded).
+            GL_ADMINDIR = self.osenv.get(
+                'GL_ADMINDIR',
+                os.path.expanduser(os.path.join('~', '.gitolite')))
+            GL_CONF = self.osenv.get(
+                'GL_CONF',
+                os.path.join(GL_ADMINDIR, 'conf', 'gitolite.conf'))
+            if os.path.isfile(GL_CONF):
+                f = open(GL_CONF, 'rU')
+                try:
+                    in_user_emails_section = False
+                    re_template = r'^\s*#\s*{}\s*$'
+                    re_begin, re_user, re_end = (
+                        re.compile(re_template.format(x))
+                        for x in (
+                            r'BEGIN\s+USER\s+EMAILS',
+                            re.escape(GL_USER) + r'\s+(.*)',
+                            r'END\s+USER\s+EMAILS',
+                            ))
+                    for l in f:
+                        l = l.rstrip('\n')
+                        if not in_user_emails_section:
+                            if re_begin.match(l):
+                                in_user_emails_section = True
+                            continue
+                        if re_end.match(l):
+                            break
+                        m = re_user.match(l)
+                        if m:
+                            return m.group(1)
+                finally:
+                    f.close()
+        return super(GitoliteEnvironmentMixin, self).get_fromaddr()
+
 
 class IncrementalDateTime(object):
     """Simple wrapper to give incremental date/times.
@@ -2116,16 +2578,16 @@
 
 
 class GitoliteEnvironment(
-    ProjectdescEnvironmentMixin,
-    ConfigMaxlinesEnvironmentMixin,
-    ComputeFQDNEnvironmentMixin,
-    ConfigFilterLinesEnvironmentMixin,
-    ConfigRecipientsEnvironmentMixin,
-    PusherDomainEnvironmentMixin,
-    ConfigOptionsEnvironmentMixin,
-    GitoliteEnvironmentMixin,
-    Environment,
-    ):
+        ProjectdescEnvironmentMixin,
+        ConfigMaxlinesEnvironmentMixin,
+        ComputeFQDNEnvironmentMixin,
+        ConfigFilterLinesEnvironmentMixin,
+        ConfigRecipientsEnvironmentMixin,
+        PusherDomainEnvironmentMixin,
+        ConfigOptionsEnvironmentMixin,
+        GitoliteEnvironmentMixin,
+        Environment,
+        ):
     pass
 
 
@@ -2149,9 +2611,9 @@
     references.
 
     The first step is to determine the "other" references--those
-    unaffected by the current push.  They are computed by
-    Push._compute_other_ref_sha1s() by listing all references then
-    removing any affected by this push.
+    unaffected by the current push.  They are computed by listing all
+    references then removing any affected by this push.  The results
+    are stored in Push._other_ref_sha1s.
 
     The commits contained in the repository before this push were
 
@@ -2187,7 +2649,7 @@
     possible and working with SHA1s thereafter (because SHA1s are
     immutable)."""
 
-    # A map {(changeclass, changetype) : integer} specifying the order
+    # A map {(changeclass, changetype): integer} specifying the order
     # that reference changes will be processed if multiple reference
     # changes are included in a single push.  The order is significant
     # mostly because new commit notifications are threaded together
@@ -2211,66 +2673,134 @@
             ])
         )
 
-    def __init__(self, changes):
+    def __init__(self, changes, ignore_other_refs=False):
         self.changes = sorted(changes, key=self._sort_key)
+        self.__other_ref_sha1s = None
+        self.__cached_commits_spec = {}
 
-        # The SHA-1s of commits referred to by references unaffected
-        # by this push:
-        other_ref_sha1s = self._compute_other_ref_sha1s()
-
-        self._old_rev_exclusion_spec = self._compute_rev_exclusion_spec(
-            other_ref_sha1s.union(
-                change.old.sha1
-                for change in self.changes
-                if change.old.type in ['commit', 'tag']
-                )
-            )
-        self._new_rev_exclusion_spec = self._compute_rev_exclusion_spec(
-            other_ref_sha1s.union(
-                change.new.sha1
-                for change in self.changes
-                if change.new.type in ['commit', 'tag']
-                )
-            )
+        if ignore_other_refs:
+            self.__other_ref_sha1s = set()
 
     @classmethod
     def _sort_key(klass, change):
         return (klass.SORT_ORDER[change.__class__, change.change_type], change.refname,)
 
-    def _compute_other_ref_sha1s(self):
-        """Return the GitObjects referred to by references unaffected by this push."""
+    @property
+    def _other_ref_sha1s(self):
+        """The GitObjects referred to by references unaffected by this push.
+        """
+        if self.__other_ref_sha1s is None:
+            # The refnames being changed by this push:
+            updated_refs = set(
+                change.refname
+                for change in self.changes
+                )
 
-        # The refnames being changed by this push:
-        updated_refs = set(
-            change.refname
+            # The SHA-1s of commits referred to by all references in this
+            # repository *except* updated_refs:
+            sha1s = set()
+            fmt = (
+                '%(objectname) %(objecttype) %(refname)\n'
+                '%(*objectname) %(*objecttype) %(refname)'
+                )
+            for line in read_git_lines(
+                    ['for-each-ref', '--format=%s' % (fmt,)]):
+                (sha1, type, name) = line.split(' ', 2)
+                if sha1 and type == 'commit' and name not in updated_refs:
+                    sha1s.add(sha1)
+
+            self.__other_ref_sha1s = sha1s
+
+        return self.__other_ref_sha1s
+
+    def _get_commits_spec_incl(self, new_or_old, reference_change=None):
+        """Get new or old SHA-1 from one or each of the changed refs.
+
+        Return a list of SHA-1 commit identifier strings suitable as
+        arguments to 'git rev-list' (or 'git log' or ...).  The
+        returned identifiers are either the old or new values from one
+        or all of the changed references, depending on the values of
+        new_or_old and reference_change.
+
+        new_or_old is either the string 'new' or the string 'old'.  If
+        'new', the returned SHA-1 identifiers are the new values from
+        each changed reference.  If 'old', the SHA-1 identifiers are
+        the old values from each changed reference.
+
+        If reference_change is specified and not None, only the new or
+        old reference from the specified reference is included in the
+        return value.
+
+        This function returns None if there are no matching revisions
+        (e.g., because a branch was deleted and new_or_old is 'new').
+        """
+
+        if not reference_change:
+            incl_spec = sorted(
+                getattr(change, new_or_old).sha1
+                for change in self.changes
+                if getattr(change, new_or_old)
+                )
+            if not incl_spec:
+                incl_spec = None
+        elif not getattr(reference_change, new_or_old).commit_sha1:
+            incl_spec = None
+        else:
+            incl_spec = [getattr(reference_change, new_or_old).commit_sha1]
+        return incl_spec
+
+    def _get_commits_spec_excl(self, new_or_old):
+        """Get exclusion revisions for determining new or discarded commits.
+
+        Return a list of strings suitable as arguments to 'git
+        rev-list' (or 'git log' or ...) that will exclude all
+        commits that, depending on the value of new_or_old, were
+        either previously in the repository (useful for determining
+        which commits are new to the repository) or currently in the
+        repository (useful for determining which commits were
+        discarded from the repository).
+
+        new_or_old is either the string 'new' or the string 'old'.  If
+        'new', the commits to be excluded are those that were in the
+        repository before the push.  If 'old', the commits to be
+        excluded are those that are currently in the repository.  """
+
+        old_or_new = {'old': 'new', 'new': 'old'}[new_or_old]
+        excl_revs = self._other_ref_sha1s.union(
+            getattr(change, old_or_new).sha1
             for change in self.changes
+            if getattr(change, old_or_new).type in ['commit', 'tag']
             )
+        return ['^' + sha1 for sha1 in sorted(excl_revs)]
 
-        # The SHA-1s of commits referred to by all references in this
-        # repository *except* updated_refs:
-        sha1s = set()
-        fmt = (
-            '%(objectname) %(objecttype) %(refname)\n'
-            '%(*objectname) %(*objecttype) %(refname)'
-            )
-        for line in read_git_lines(['for-each-ref', '--format=%s' % (fmt,)]):
-            (sha1, type, name) = line.split(' ', 2)
-            if sha1 and type == 'commit' and name not in updated_refs:
-                sha1s.add(sha1)
+    def get_commits_spec(self, new_or_old, reference_change=None):
+        """Get rev-list arguments for added or discarded commits.
 
-        return sha1s
+        Return a list of strings suitable as arguments to 'git
+        rev-list' (or 'git log' or ...) that select those commits
+        that, depending on the value of new_or_old, are either new to
+        the repository or were discarded from the repository.
 
-    def _compute_rev_exclusion_spec(self, sha1s):
-        """Return an exclusion specification for 'git rev-list'.
+        new_or_old is either the string 'new' or the string 'old'.  If
+        'new', the returned list is used to select commits that are
+        new to the repository.  If 'old', the returned value is used
+        to select the commits that have been discarded from the
+        repository.
 
-        git_objects is an iterable over GitObject instances.  Return a
-        string that can be passed to the standard input of 'git
-        rev-list --stdin' to exclude all of the commits referred to by
-        git_objects."""
+        If reference_change is specified and not None, the new or
+        discarded commits are limited to those that are reachable from
+        the new or old value of the specified reference.
 
-        return ''.join(
-            ['^%s\n' % (sha1,) for sha1 in sorted(sha1s)]
-            )
+        This function returns None if there are no added (or discarded)
+        revisions.
+        """
+        key = (new_or_old, reference_change)
+        if key not in self.__cached_commits_spec:
+            ret = self._get_commits_spec_incl(new_or_old, reference_change)
+            if ret is not None:
+                ret.extend(self._get_commits_spec_excl(new_or_old))
+            self.__cached_commits_spec[key] = ret
+        return self.__cached_commits_spec[key]
 
     def get_new_commits(self, reference_change=None):
         """Return a list of commits added by this push.
@@ -2280,19 +2810,8 @@
         reference_change is None, then return a list of *all* commits
         added by this push."""
 
-        if not reference_change:
-            new_revs = sorted(
-                change.new.sha1
-                for change in self.changes
-                if change.new
-                )
-        elif not reference_change.new.commit_sha1:
-            return []
-        else:
-            new_revs = [reference_change.new.commit_sha1]
-
-        cmd = ['rev-list', '--stdin'] + new_revs
-        return read_git_lines(cmd, input=self._old_rev_exclusion_spec)
+        spec = self.get_commits_spec('new', reference_change)
+        return git_rev_list(spec)
 
     def get_discarded_commits(self, reference_change):
         """Return a list of commits discarded by this push.
@@ -2301,13 +2820,8 @@
         entirely discarded from the repository by the part of this
         push represented by reference_change."""
 
-        if not reference_change.old.commit_sha1:
-            return []
-        else:
-            old_revs = [reference_change.old.commit_sha1]
-
-        cmd = ['rev-list', '--stdin'] + old_revs
-        return read_git_lines(cmd, input=self._new_rev_exclusion_spec)
+        spec = self.get_commits_spec('old', reference_change)
+        return git_rev_list(spec)
 
     def send_emails(self, mailer, body_filter=None):
         """Use send all of the notification emails needed for this push.
@@ -2325,30 +2839,43 @@
         unhandled_sha1s = set(self.get_new_commits())
         send_date = IncrementalDateTime()
         for change in self.changes:
-            # Check if we've got anyone to send to
-            if not change.recipients:
-                sys.stderr.write(
-                    '*** no recipients configured so no email will be sent\n'
-                    '*** for %r update %s->%s\n'
-                    % (change.refname, change.old.sha1, change.new.sha1,)
-                    )
-            else:
-                sys.stderr.write('Sending notification emails to: %s\n' % (change.recipients,))
-                extra_values = {'send_date' : send_date.next()}
-                mailer.send(
-                    change.generate_email(self, body_filter, extra_values),
-                    change.recipients,
-                    )
-
             sha1s = []
             for sha1 in reversed(list(self.get_new_commits(change))):
                 if sha1 in unhandled_sha1s:
                     sha1s.append(sha1)
                     unhandled_sha1s.remove(sha1)
 
+            # Check if we've got anyone to send to
+            if not change.recipients:
+                change.environment.log_warning(
+                    '*** no recipients configured so no email will be sent\n'
+                    '*** for %r update %s->%s\n'
+                    % (change.refname, change.old.sha1, change.new.sha1,)
+                    )
+            else:
+                if not change.environment.quiet:
+                    change.environment.log_msg(
+                        'Sending notification emails to: %s\n' % (change.recipients,))
+                extra_values = {'send_date': send_date.next()}
+
+                rev = change.send_single_combined_email(sha1s)
+                if rev:
+                    mailer.send(
+                        change.generate_combined_email(self, rev, body_filter, extra_values),
+                        rev.recipients,
+                        )
+                    # This change is now fully handled; no need to handle
+                    # individual revisions any further.
+                    continue
+                else:
+                    mailer.send(
+                        change.generate_email(self, body_filter, extra_values),
+                        change.recipients,
+                        )
+
             max_emails = change.environment.maxcommitemails
             if max_emails and len(sha1s) > max_emails:
-                sys.stderr.write(
+                change.environment.log_warning(
                     '*** Too many new commits (%d), not sending commit emails.\n' % len(sha1s)
                     + '*** Try setting multimailhook.maxCommitEmails to a greater value\n'
                     + '*** Currently, multimailhook.maxCommitEmails=%d\n' % max_emails
@@ -2356,9 +2883,13 @@
                 return
 
             for (num, sha1) in enumerate(sha1s):
-                rev = Revision(change, GitObject(sha1), num=num+1, tot=len(sha1s))
+                rev = Revision(change, GitObject(sha1), num=num + 1, tot=len(sha1s))
+                if not rev.recipients and rev.cc_recipients:
+                    change.environment.log_msg('*** Replacing Cc: with To:\n')
+                    rev.recipients = rev.cc_recipients
+                    rev.cc_recipients = None
                 if rev.recipients:
-                    extra_values = {'send_date' : send_date.next()}
+                    extra_values = {'send_date': send_date.next()}
                     mailer.send(
                         rev.generate_email(self, body_filter, extra_values),
                         rev.recipients,
@@ -2366,7 +2897,7 @@
 
         # Consistency check:
         if unhandled_sha1s:
-            sys.stderr.write(
+            change.environment.log_error(
                 'ERROR: No emails were sent for the following new commits:\n'
                 '    %s\n'
                 % ('\n    '.join(sorted(unhandled_sha1s)),)
@@ -2384,7 +2915,7 @@
     push.send_emails(mailer, body_filter=environment.filter_body)
 
 
-def run_as_update_hook(environment, mailer, refname, oldrev, newrev):
+def run_as_update_hook(environment, mailer, refname, oldrev, newrev, force_send=False):
     changes = [
         ReferenceChange.create(
             environment,
@@ -2393,7 +2924,7 @@
             refname,
             ),
         ]
-    push = Push(changes)
+    push = Push(changes, force_send)
     push.send_emails(mailer, body_filter=environment.filter_body)
 
 
@@ -2402,9 +2933,18 @@
 
     if mailer == 'smtp':
         smtpserver = config.get('smtpserver', default='localhost')
+        smtpservertimeout = float(config.get('smtpservertimeout', default=10.0))
+        smtpserverdebuglevel = int(config.get('smtpserverdebuglevel', default=0))
+        smtpencryption = config.get('smtpencryption', default='none')
+        smtpuser = config.get('smtpuser', default='')
+        smtppass = config.get('smtppass', default='')
         mailer = SMTPMailer(
             envelopesender=(environment.get_sender() or environment.get_fromaddr()),
-            smtpserver=smtpserver,
+            smtpserver=smtpserver, smtpservertimeout=smtpservertimeout,
+            smtpserverdebuglevel=smtpserverdebuglevel,
+            smtpencryption=smtpencryption,
+            smtpuser=smtpuser,
+            smtppass=smtppass,
             )
     elif mailer == 'sendmail':
         command = config.get('sendmailcommand')
@@ -2412,7 +2952,7 @@
             command = shlex.split(command)
         mailer = SendMailer(command=command, envelopesender=environment.get_sender())
     else:
-        sys.stderr.write(
+        environment.log_error(
             'fatal: multimailhook.mailer is set to an incorrect value: "%s"\n' % mailer
             + 'please use one of "smtp" or "sendmail".\n'
             )
@@ -2421,8 +2961,8 @@
 
 
 KNOWN_ENVIRONMENTS = {
-    'generic' : GenericEnvironmentMixin,
-    'gitolite' : GitoliteEnvironmentMixin,
+    'generic': GenericEnvironmentMixin,
+    'gitolite': GitoliteEnvironmentMixin,
     }
 
 
@@ -2439,8 +2979,8 @@
         ConfigOptionsEnvironmentMixin,
         ]
     environment_kw = {
-        'osenv' : osenv,
-        'config' : config,
+        'osenv': osenv,
+        'config': config,
         }
 
     if not env:
@@ -2459,6 +2999,7 @@
         environment_kw['refchange_recipients'] = recipients
         environment_kw['announce_recipients'] = recipients
         environment_kw['revision_recipients'] = recipients
+        environment_kw['scancommitforcc'] = config.get('scancommitforcc')
     else:
         environment_mixins.insert(0, ConfigRecipientsEnvironmentMixin)
 
@@ -2499,6 +3040,14 @@
             '(intended for debugging purposes).'
             ),
         )
+    parser.add_option(
+        '--force-send', action='store_true', default=False,
+        help=(
+            'Force sending refchange email when using as an update hook. '
+            'This is useful to work around the unreliable new commits '
+            'detection in this mode.'
+            ),
+        )
 
     (options, args) = parser.parse_args(args)
 
@@ -2513,11 +3062,11 @@
 
         if options.show_env:
             sys.stderr.write('Environment values:\n')
-            for (k,v) in sorted(environment.get_values().items()):
-                sys.stderr.write('    %s : %r\n' % (k,v))
+            for (k, v) in sorted(environment.get_values().items()):
+                sys.stderr.write('    %s : %r\n' % (k, v))
             sys.stderr.write('\n')
 
-        if options.stdout:
+        if options.stdout or environment.stdout:
             mailer = OutputMailer(sys.stdout)
         else:
             mailer = choose_mailer(config, environment)
@@ -2528,7 +3077,7 @@
             if len(args) != 3:
                 parser.error('Need zero or three non-option arguments')
             (refname, oldrev, newrev) = args
-            run_as_update_hook(environment, mailer, refname, oldrev, newrev)
+            run_as_update_hook(environment, mailer, refname, oldrev, newrev, options.force_send)
         else:
             run_as_post_receive_hook(environment, mailer)
     except ConfigurationException, e:
diff --git a/contrib/hooks/multimail/migrate-mailhook-config b/contrib/hooks/multimail/migrate-mailhook-config
index 04eeaac..d0e9b39 100755
--- a/contrib/hooks/multimail/migrate-mailhook-config
+++ b/contrib/hooks/multimail/migrate-mailhook-config
@@ -22,6 +22,7 @@
     'showrev',
     'emailmaxlines',
     'diffopts',
+    'scancommitforcc',
     ]
 
 NEW_NAMES = [
@@ -38,6 +39,7 @@
     'emailmaxlines',
     'diffopts',
     'emaildomain',
+    'scancommitforcc',
     ]
 
 
@@ -61,7 +63,7 @@
     """Check that at least one old configuration value is set."""
 
     for name in OLD_NAMES:
-        if old.has_key(name):
+        if name in old:
             return True
 
     return False
@@ -72,7 +74,7 @@
 
     retval = True
     for name in NEW_NAMES:
-        if new.has_key(name):
+        if name in new:
             if retval:
                 sys.stderr.write('INFO: The following configuration values already exist:\n\n')
             sys.stderr.write('    "%s.%s"\n' % (new.section, name))
@@ -83,7 +85,7 @@
 
 def erase_values(config, names):
     for name in names:
-        if config.has_key(name):
+        if name in config:
             try:
                 sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name))
                 config.unset_all(name)
@@ -170,7 +172,7 @@
                 )
 
     name = 'showrev'
-    if old.has_key(name):
+    if name in old:
         msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
         if strict:
             sys.exit(
@@ -182,7 +184,7 @@
             sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
 
     for name in ['mailinglist', 'announcelist']:
-        if old.has_key(name):
+        if name in old:
             sys.stderr.write(
                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
                 )
@@ -198,15 +200,15 @@
             )
         new.set('announceshortlog', 'true')
 
-    for name in ['envelopesender', 'emailmaxlines', 'diffopts']:
-        if old.has_key(name):
+    for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']:
+        if name in old:
             sys.stderr.write(
                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
                 )
             new.set(name, old.get(name))
 
     name = 'emailprefix'
-    if old.has_key(name):
+    if name in old:
         sys.stderr.write(
             '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
             )
diff --git a/contrib/hooks/multimail/post-receive b/contrib/hooks/multimail/post-receive.example
similarity index 74%
rename from contrib/hooks/multimail/post-receive
rename to contrib/hooks/multimail/post-receive.example
index 4d46828..43f7b6b 100755
--- a/contrib/hooks/multimail/post-receive
+++ b/contrib/hooks/multimail/post-receive.example
@@ -2,16 +2,18 @@
 
 """Example post-receive hook based on git-multimail.
 
-This script is a simple example of a post-receive hook implemented
-using git_multimail.py as a Python module.  It is intended to be
-customized before use; see the comments in the script to help you get
-started.
+The simplest way to use git-multimail is to use the script
+git_multimail.py directly as a post-receive hook, and to configure it
+using Git's configuration files and command-line parameters.  You can
+also write your own Python wrapper for more advanced configurability,
+using git_multimail.py as a Python module.
 
-It is possible to use git_multimail.py itself as a post-receive or
-update hook, configured via git config settings and/or command-line
-parameters.  But for more flexibility, it can also be imported as a
-Python module by a custom post-receive script as done here.  The
-latter has the following advantages:
+This script is a simple example of such a post-receive hook.  It is
+intended to be customized before use; see the comments in the script
+to help you get started.
+
+Using git-multimail as a Python module as done here provides more
+flexibility.  It has the following advantages:
 
 * The tool's behavior can be customized using arbitrary Python code,
   without having to edit git_multimail.py.
@@ -56,8 +58,11 @@
 
 
 # Select the type of environment:
-environment = git_multimail.GenericEnvironment(config=config)
-#environment = git_multimail.GitoliteEnvironment(config=config)
+try:
+    environment = git_multimail.GenericEnvironment(config=config)
+    #environment = git_multimail.GitoliteEnvironment(config=config)
+except git_multimail.ConfigurationException, e:
+    sys.exit(str(e))
 
 
 # Choose the method of sending emails based on the git config:
diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery
index 9d0c2d1..6a2cdeb 100755
--- a/contrib/hooks/pre-auto-gc-battery
+++ b/contrib/hooks/pre-auto-gc-battery
@@ -33,7 +33,7 @@
 then
 	exit 0
 elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |
-	grep -q "Currently drawing from 'AC Power'"
+	grep -q "drawing from 'AC Power'"
 then
 	exit 0
 fi
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index fa1a583..07bd77c 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
Binary files differ
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
index 54e4b4a..60d76cd 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.txt
@@ -146,7 +146,7 @@
 OPTIONS FOR add, merge, push, pull
 ----------------------------------
 --squash::
-	This option is only valid for add, merge, push and pull
+	This option is only valid for add, merge, and pull
 	commands.
 +
 Instead of merging the entire history from the subtree project, produce
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 6309d12..bd3df97 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -62,17 +62,17 @@
 }
 
 test_expect_success 'init subproj' '
-        test_create_repo subproj
+	test_create_repo subproj
 '
 
 # To the subproject!
 cd subproj
 
 test_expect_success 'add sub1' '
-        create sub1 &&
-        git commit -m "sub1" &&
-        git branch sub1 &&
-        git branch -m master subproj
+	create sub1 &&
+	git commit -m "sub1" &&
+	git branch sub1 &&
+	git branch -m master subproj
 '
 
 # Save this hash for testing later.
@@ -80,133 +80,134 @@
 subdir_hash=$(git rev-parse HEAD)
 
 test_expect_success 'add sub2' '
-        create sub2 &&
-        git commit -m "sub2" &&
-        git branch sub2
+	create sub2 &&
+	git commit -m "sub2" &&
+	git branch sub2
 '
 
 test_expect_success 'add sub3' '
-        create sub3 &&
-        git commit -m "sub3" &&
-        git branch sub3
+	create sub3 &&
+	git commit -m "sub3" &&
+	git branch sub3
 '
 
 # Back to mainline
 cd ..
 
 test_expect_success 'add main4' '
-        create main4 &&
-        git commit -m "main4" &&
-        git branch -m master mainline &&
-        git branch subdir
+	create main4 &&
+	git commit -m "main4" &&
+	git branch -m master mainline &&
+	git branch subdir
 '
 
 test_expect_success 'fetch subproj history' '
-        git fetch ./subproj sub1 &&
-        git branch sub1 FETCH_HEAD
+	git fetch ./subproj sub1 &&
+	git branch sub1 FETCH_HEAD
 '
 
 test_expect_success 'no subtree exists in main tree' '
-        test_must_fail git subtree merge --prefix=subdir sub1
+	test_must_fail git subtree merge --prefix=subdir sub1
 '
 
 test_expect_success 'no pull from non-existant subtree' '
-        test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+	test_must_fail git subtree pull --prefix=subdir ./subproj sub1
 '
 
 test_expect_success 'check if --message works for add' '
-        git subtree add --prefix=subdir --message="Added subproject" sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject" &&
-        undo
+	git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+	check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+	undo
 '
 
 test_expect_success 'check if --message works as -m and --prefix as -P' '
-        git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
-        undo
+	git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+	check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+	undo
 '
 
 test_expect_success 'check if --message works with squash too' '
-        git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
-        check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
-        undo
+	git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+	check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+	undo
 '
 
 test_expect_success 'add subproj to mainline' '
-        git subtree add --prefix=subdir/ FETCH_HEAD &&
-        check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+	git subtree add --prefix=subdir/ FETCH_HEAD &&
+	check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
 '
 
 # this shouldn't actually do anything, since FETCH_HEAD is already a parent
 test_expect_success 'merge fetched subproj' '
-        git merge -m "merge -s -ours" -s ours FETCH_HEAD
+	git merge -m "merge -s -ours" -s ours FETCH_HEAD
 '
 
 test_expect_success 'add main-sub5' '
-        create subdir/main-sub5 &&
-        git commit -m "main-sub5"
+	create subdir/main-sub5 &&
+	git commit -m "main-sub5"
 '
 
 test_expect_success 'add main6' '
-        create main6 &&
-        git commit -m "main6 boring"
+	create main6 &&
+	git commit -m "main6 boring"
 '
 
 test_expect_success 'add main-sub7' '
-        create subdir/main-sub7 &&
-        git commit -m "main-sub7"
+	create subdir/main-sub7 &&
+	git commit -m "main-sub7"
 '
 
 test_expect_success 'fetch new subproj history' '
-        git fetch ./subproj sub2 &&
-        git branch sub2 FETCH_HEAD
+	git fetch ./subproj sub2 &&
+	git branch sub2 FETCH_HEAD
 '
 
 test_expect_success 'check if --message works for merge' '
-        git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
-        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
-        undo
+	git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+	check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+	undo
 '
 
 test_expect_success 'check if --message for merge works with squash too' '
-        git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
-        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
-        undo
+	git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+	check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+	undo
 '
 
 test_expect_success 'merge new subproj history into subdir' '
-        git subtree merge --prefix=subdir FETCH_HEAD &&
-        git branch pre-split &&
-        check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+	git subtree merge --prefix=subdir FETCH_HEAD &&
+	git branch pre-split &&
+	check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" &&
+	undo
 '
 
 test_expect_success 'Check that prefix argument is required for split' '
-        echo "You must provide the --prefix option." > expected &&
-        test_must_fail git subtree split > actual 2>&1 &&
+	echo "You must provide the --prefix option." > expected &&
+	test_must_fail git subtree split > actual 2>&1 &&
 	test_debug "printf '"'"'expected: '"'"'" &&
-        test_debug "cat expected" &&
+	test_debug "cat expected" &&
 	test_debug "printf '"'"'actual: '"'"'" &&
-        test_debug "cat actual" &&
-        test_cmp expected actual &&
-        rm -f expected actual
+	test_debug "cat actual" &&
+	test_cmp expected actual &&
+	rm -f expected actual
 '
 
 test_expect_success 'Check that the <prefix> exists for a split' '
-        echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
-        test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
+	echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
+	test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
 	test_debug "printf '"'"'expected: '"'"'" &&
-        test_debug "cat expected" &&
+	test_debug "cat expected" &&
 	test_debug "printf '"'"'actual: '"'"'" &&
-        test_debug "cat actual" &&
-        test_cmp expected actual
-#        rm -f expected actual
+	test_debug "cat actual" &&
+	test_cmp expected actual
+#	rm -f expected actual
 '
 
 test_expect_success 'check if --message works for split+rejoin' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        git branch spl1 "$spl1" &&
-        check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
-        undo
+	spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+	git branch spl1 "$spl1" &&
+	check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+	undo
 '
 
 test_expect_success 'check split with --branch' '
@@ -218,79 +219,76 @@
 
 test_expect_success 'check hash of split' '
 	spl1=$(git subtree split --prefix subdir) &&
-	undo &&
 	git subtree split --prefix subdir --branch splitbr1test &&
-	check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1"
-	git checkout splitbr1test &&
-	new_hash=$(git rev-parse HEAD~2) &&
-	git checkout mainline &&
+	check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" &&
+	new_hash=$(git rev-parse splitbr1test~2) &&
 	check_equal ''"$new_hash"'' "$subdir_hash"
 '
 
 test_expect_success 'check split with --branch for an existing branch' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        undo &&
-        git branch splitbr2 sub1 &&
-        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
-        check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+	spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+	undo &&
+	git branch splitbr2 sub1 &&
+	git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+	check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
 '
 
 test_expect_success 'check split with --branch for an incompatible branch' '
-        test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+	test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
 '
 
 test_expect_success 'check split+rejoin' '
-        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-        undo &&
-        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
-        check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+	spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+	undo &&
+	git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
+	check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
 '
 
 test_expect_success 'add main-sub8' '
-        create subdir/main-sub8 &&
-        git commit -m "main-sub8"
+	create subdir/main-sub8 &&
+	git commit -m "main-sub8"
 '
 
 # To the subproject!
 cd ./subproj
 
 test_expect_success 'merge split into subproj' '
-        git fetch .. spl1 &&
-        git branch spl1 FETCH_HEAD &&
-        git merge FETCH_HEAD
+	git fetch .. spl1 &&
+	git branch spl1 FETCH_HEAD &&
+	git merge FETCH_HEAD
 '
 
 test_expect_success 'add sub9' '
-        create sub9 &&
-        git commit -m "sub9"
+	create sub9 &&
+	git commit -m "sub9"
 '
 
 # Back to mainline
 cd ..
 
 test_expect_success 'split for sub8' '
-        split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
-        git branch split2 "$split2"
+	split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' &&
+	git branch split2 "$split2"
 '
 
 test_expect_success 'add main-sub10' '
-        create subdir/main-sub10 &&
-        git commit -m "main-sub10"
+	create subdir/main-sub10 &&
+	git commit -m "main-sub10"
 '
 
 test_expect_success 'split for sub10' '
-        spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
-        git branch spl3 "$spl3"
+	spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+	git branch spl3 "$spl3"
 '
 
 # To the subproject!
 cd ./subproj
 
 test_expect_success 'merge split into subproj' '
-        git fetch .. spl3 &&
-        git branch spl3 FETCH_HEAD &&
-        git merge FETCH_HEAD &&
-        git branch subproj-merge-spl3
+	git fetch .. spl3 &&
+	git branch spl3 FETCH_HEAD &&
+	git merge FETCH_HEAD &&
+	git branch subproj-merge-spl3
 '
 
 chkm="main4 main6"
@@ -300,44 +298,44 @@
 chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
 
 test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
-        subfiles=''"$(git ls-files | fixnl)"'' &&
-        check_equal "$subfiles" "$chkms $chks"
+	subfiles=''"$(git ls-files | fixnl)"'' &&
+	check_equal "$subfiles" "$chkms $chks"
 '
 
 test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
-        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-        check_equal "$allchanges" "$chkms $chks"
+	allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+	check_equal "$allchanges" "$chkms $chks"
 '
 
 # Back to mainline
 cd ..
 
 test_expect_success 'pull from subproj' '
-        git fetch ./subproj subproj-merge-spl3 &&
-        git branch subproj-merge-spl3 FETCH_HEAD &&
-        git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+	git fetch ./subproj subproj-merge-spl3 &&
+	git branch subproj-merge-spl3 FETCH_HEAD &&
+	git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
 '
 
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
-        mainfiles=''"$(git ls-files | fixnl)"'' &&
-        check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+	mainfiles=''"$(git ls-files | fixnl)"'' &&
+	check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
 '
 
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
-        # main-sub?? and /subdir/main-sub?? both change, because those are the
-        # changes that were split into their own history.  And subdir/sub?? never
-        # change, since they were *only* changed in the subtree branch.
-        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
-        check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+	# main-sub?? and /subdir/main-sub?? both change, because those are the
+	# changes that were split into their own history.  And subdir/sub?? never
+	# change, since they were *only* changed in the subtree branch.
+	allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+	check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
 '
 
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
-        check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+	check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
 '
 
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
-        # They are meaningless to subproj since one side of the merge refers to the mainline
-        check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+	# They are meaningless to subproj since one side of the merge refers to the mainline
+	check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
 '
 
 # prepare second pair of repositories
@@ -345,27 +343,27 @@
 cd test2
 
 test_expect_success 'init main' '
-        test_create_repo main
+	test_create_repo main
 '
 
 cd main
 
 test_expect_success 'add main1' '
-        create main1 &&
-        git commit -m "main1"
+	create main1 &&
+	git commit -m "main1"
 '
 
 cd ..
 
 test_expect_success 'init sub' '
-        test_create_repo sub
+	test_create_repo sub
 '
 
 cd sub
 
 test_expect_success 'add sub2' '
-        create sub2 &&
-        git commit -m "sub2"
+	create sub2 &&
+	git commit -m "sub2"
 '
 
 cd ../main
@@ -373,33 +371,33 @@
 # check if split can find proper base without --onto
 
 test_expect_success 'add sub as subdir in main' '
-        git fetch ../sub master &&
-        git branch sub2 FETCH_HEAD &&
-        git subtree add --prefix subdir sub2
+	git fetch ../sub master &&
+	git branch sub2 FETCH_HEAD &&
+	git subtree add --prefix subdir sub2
 '
 
 cd ../sub
 
 test_expect_success 'add sub3' '
-        create sub3 &&
-        git commit -m "sub3"
+	create sub3 &&
+	git commit -m "sub3"
 '
 
 cd ../main
 
 test_expect_success 'merge from sub' '
-        git fetch ../sub master &&
-        git branch sub3 FETCH_HEAD &&
-        git subtree merge --prefix subdir sub3
+	git fetch ../sub master &&
+	git branch sub3 FETCH_HEAD &&
+	git subtree merge --prefix subdir sub3
 '
 
 test_expect_success 'add main-sub4' '
-        create subdir/main-sub4 &&
-        git commit -m "main-sub4"
+	create subdir/main-sub4 &&
+	git commit -m "main-sub4"
 '
 
 test_expect_success 'split for main-sub4 without --onto' '
-        git subtree split --prefix subdir --branch mainsub4
+	git subtree split --prefix subdir --branch mainsub4
 '
 
 # at this point, the new commit parent should be sub3 if it is not,
@@ -408,21 +406,21 @@
 # itself)
 
 test_expect_success 'check that the commit parent is sub3' '
-        check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+	check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
 '
 
 test_expect_success 'add main-sub5' '
-        mkdir subdir2 &&
-        create subdir2/main-sub5 &&
-        git commit -m "main-sub5"
+	mkdir subdir2 &&
+	create subdir2/main-sub5 &&
+	git commit -m "main-sub5"
 '
 
 test_expect_success 'split for main-sub5 without --onto' '
-        # also test that we still can split out an entirely new subtree
-        # if the parent of the first commit in the tree is not empty,
+	# also test that we still can split out an entirely new subtree
+	# if the parent of the first commit in the tree is not empty,
 	# then the new subtree has accidentally been attached to something
-        git subtree split --prefix subdir2 --branch mainsub5 &&
-        check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+	git subtree split --prefix subdir2 --branch mainsub5 &&
+	check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
 '
 
 # make sure no patch changes more than one file.  The original set of commits
@@ -450,20 +448,20 @@
 }
 
 test_expect_success 'verify one file change per commit' '
-        x= &&
-        list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-#        test_debug "echo HERE" &&
-#        test_debug "echo ''"$list"''" &&
-        (git log --pretty=format:'"'commit: %H'"' | joincommits |
-        (       while read commit a b; do
-		        test_debug "echo Verifying commit "''"$commit"''
-		        test_debug "echo a: "''"$a"''
-		        test_debug "echo b: "''"$b"''
-		        check_equal "$b" ""
-		        x=1
-	        done
-	        check_equal "$x" 1
-        ))
+	x= &&
+	list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
+#	test_debug "echo HERE" &&
+#	test_debug "echo ''"$list"''" &&
+	(git log --pretty=format:'"'commit: %H'"' | joincommits |
+	(	while read commit a b; do
+			test_debug "echo Verifying commit "''"$commit"''
+			test_debug "echo a: "''"$a"''
+			test_debug "echo b: "''"$b"''
+			check_equal "$b" ""
+			x=1
+		done
+		check_equal "$x" 1
+	))
 '
 
 test_done
diff --git a/convert.c b/convert.c
index 9a5612e..f3bd3e9 100644
--- a/convert.c
+++ b/convert.c
@@ -356,9 +356,14 @@
 	sigchain_push(SIGPIPE, SIG_IGN);
 
 	if (params->src) {
-		write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+		write_err = (write_in_full(child_process.in,
+					   params->src, params->size) < 0);
+		if (errno == EPIPE)
+			write_err = 0;
 	} else {
 		write_err = copy_fd(params->fd, child_process.in);
+		if (write_err == COPY_WRITE_ERROR && errno == EPIPE)
+			write_err = 0;
 	}
 
 	if (close(child_process.in))
diff --git a/copy.c b/copy.c
index f2970ec..574fa1f 100644
--- a/copy.c
+++ b/copy.c
@@ -7,13 +7,10 @@
 		ssize_t len = xread(ifd, buffer, sizeof(buffer));
 		if (!len)
 			break;
-		if (len < 0) {
-			return error("copy-fd: read returned %s",
-				     strerror(errno));
-		}
+		if (len < 0)
+			return COPY_READ_ERROR;
 		if (write_in_full(ofd, buffer, len) < 0)
-			return error("copy-fd: write returned %s",
-				     strerror(errno));
+			return COPY_WRITE_ERROR;
 	}
 	return 0;
 }
@@ -43,6 +40,14 @@
 		return fdo;
 	}
 	status = copy_fd(fdi, fdo);
+	switch (status) {
+	case COPY_READ_ERROR:
+		error("copy-fd: read returned %s", strerror(errno));
+		break;
+	case COPY_WRITE_ERROR:
+		error("copy-fd: write returned %s", strerror(errno));
+		break;
+	}
 	close(fdi);
 	if (close(fdo) != 0)
 		return error("%s: close error: %s", dst, strerror(errno));
diff --git a/daemon.c b/daemon.c
index 4be1091..d3d3e43 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1166,15 +1166,6 @@
 }
 #endif
 
-static void store_pid(const char *path)
-{
-	FILE *f = fopen(path, "w");
-	if (!f)
-		die_errno("cannot open pid file '%s'", path);
-	if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
-		die_errno("failed to write pid file '%s'", path);
-}
-
 static int serve(struct string_list *listen_addr, int listen_port,
     struct credentials *cred)
 {
@@ -1385,7 +1376,7 @@
 		sanitize_stdfds();
 
 	if (pid_file)
-		store_pid(pid_file);
+		write_file(pid_file, 1, "%"PRIuMAX"\n", (uintmax_t) getpid());
 
 	/* prepare argv for serving-processes */
 	cld_argv = xmalloc(sizeof (char *) * (argc + 2));
diff --git a/diff-lib.c b/diff-lib.c
index a85c497..241a843 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -125,7 +125,7 @@
 			dpath->next = NULL;
 			memcpy(dpath->path, ce->name, path_len);
 			dpath->path[path_len] = '\0';
-			hashclr(dpath->sha1);
+			oidclr(&dpath->oid);
 			memset(&(dpath->parent[0]), 0,
 			       sizeof(struct combine_diff_parent)*5);
 
@@ -155,7 +155,7 @@
 				if (2 <= stage) {
 					int mode = nce->ce_mode;
 					num_compare_stages++;
-					hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
+					hashcpy(dpath->parent[stage-2].oid.hash, nce->sha1);
 					dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
 					dpath->parent[stage-2].status =
 						DIFF_STATUS_MODIFIED;
@@ -339,14 +339,14 @@
 		memcpy(p->path, new->name, pathlen);
 		p->path[pathlen] = 0;
 		p->mode = mode;
-		hashclr(p->sha1);
+		oidclr(&p->oid);
 		memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
 		p->parent[0].status = DIFF_STATUS_MODIFIED;
 		p->parent[0].mode = new->ce_mode;
-		hashcpy(p->parent[0].sha1, new->sha1);
+		hashcpy(p->parent[0].oid.hash, new->sha1);
 		p->parent[1].status = DIFF_STATUS_MODIFIED;
 		p->parent[1].mode = old->ce_mode;
-		hashcpy(p->parent[1].sha1, old->sha1);
+		hashcpy(p->parent[1].oid.hash, old->sha1);
 		show_combined_diff(p, 2, revs->dense_combined_merges, revs);
 		free(p);
 		return 0;
diff --git a/diff.c b/diff.c
index 100773f..87b16d5 100644
--- a/diff.c
+++ b/diff.c
@@ -478,24 +478,57 @@
 	return ws_blank_line(line, len, ecbdata->ws_rule);
 }
 
+static void emit_line_checked(const char *reset,
+			      struct emit_callback *ecbdata,
+			      const char *line, int len,
+			      enum color_diff color,
+			      unsigned ws_error_highlight,
+			      char sign)
+{
+	const char *set = diff_get_color(ecbdata->color_diff, color);
+	const char *ws = NULL;
+
+	if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
+		ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+		if (!*ws)
+			ws = NULL;
+	}
+
+	if (!ws)
+		emit_line_0(ecbdata->opt, set, reset, sign, line, len);
+	else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
+		/* Blank line at EOF - paint '+' as well */
+		emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
+	else {
+		/* Emit just the prefix, then the rest. */
+		emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
+		ws_check_emit(line, len, ecbdata->ws_rule,
+			      ecbdata->opt->file, set, reset, ws);
+	}
+}
+
 static void emit_add_line(const char *reset,
 			  struct emit_callback *ecbdata,
 			  const char *line, int len)
 {
-	const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
-	const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+	emit_line_checked(reset, ecbdata, line, len,
+			  DIFF_FILE_NEW, WSEH_NEW, '+');
+}
 
-	if (!*ws)
-		emit_line_0(ecbdata->opt, set, reset, '+', line, len);
-	else if (new_blank_line_at_eof(ecbdata, line, len))
-		/* Blank line at EOF - paint '+' as well */
-		emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
-	else {
-		/* Emit just the prefix, then the rest. */
-		emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
-		ws_check_emit(line, len, ecbdata->ws_rule,
-			      ecbdata->opt->file, set, reset, ws);
-	}
+static void emit_del_line(const char *reset,
+			  struct emit_callback *ecbdata,
+			  const char *line, int len)
+{
+	emit_line_checked(reset, ecbdata, line, len,
+			  DIFF_FILE_OLD, WSEH_OLD, '-');
+}
+
+static void emit_context_line(const char *reset,
+			      struct emit_callback *ecbdata,
+			      const char *line, int len)
+{
+	emit_line_checked(reset, ecbdata, line, len,
+			  DIFF_CONTEXT, WSEH_CONTEXT, ' ');
 }
 
 static void emit_hunk_header(struct emit_callback *ecbdata,
@@ -603,7 +636,6 @@
 {
 	const char *endp = NULL;
 	static const char *nneof = " No newline at end of file\n";
-	const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
 	const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
 
 	while (0 < size) {
@@ -613,8 +645,7 @@
 		len = endp ? (endp - data + 1) : size;
 		if (prefix != '+') {
 			ecb->lno_in_preimage++;
-			emit_line_0(ecb->opt, old, reset, '-',
-				    data, len);
+			emit_del_line(reset, ecb, data, len);
 		} else {
 			ecb->lno_in_postimage++;
 			emit_add_line(reset, ecb, data, len);
@@ -1250,17 +1281,27 @@
 		return;
 	}
 
-	if (line[0] != '+') {
-		const char *color =
-			diff_get_color(ecbdata->color_diff,
-				       line[0] == '-' ? DIFF_FILE_OLD : DIFF_CONTEXT);
-		ecbdata->lno_in_preimage++;
-		if (line[0] == ' ')
-			ecbdata->lno_in_postimage++;
-		emit_line(ecbdata->opt, color, reset, line, len);
-	} else {
+	switch (line[0]) {
+	case '+':
 		ecbdata->lno_in_postimage++;
 		emit_add_line(reset, ecbdata, line + 1, len - 1);
+		break;
+	case '-':
+		ecbdata->lno_in_preimage++;
+		emit_del_line(reset, ecbdata, line + 1, len - 1);
+		break;
+	case ' ':
+		ecbdata->lno_in_postimage++;
+		ecbdata->lno_in_preimage++;
+		emit_context_line(reset, ecbdata, line + 1, len - 1);
+		break;
+	default:
+		/* incomplete line at the end */
+		ecbdata->lno_in_preimage++;
+		emit_line(ecbdata->opt,
+			  diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
+			  reset, line, len);
+		break;
 	}
 }
 
@@ -3223,6 +3264,7 @@
 	options->rename_limit = -1;
 	options->dirstat_permille = diff_dirstat_permille_default;
 	options->context = diff_context_default;
+	options->ws_error_highlight = WSEH_NEW;
 	DIFF_OPT_SET(options, RENAME_EMPTY);
 
 	/* pathchange left =NULL by default */
@@ -3609,6 +3651,40 @@
 	*fmt |= DIFF_FORMAT_PATCH;
 }
 
+static int parse_one_token(const char **arg, const char *token)
+{
+	return skip_prefix(*arg, token, arg) && (!**arg || **arg == ',');
+}
+
+static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
+{
+	const char *orig_arg = arg;
+	unsigned val = 0;
+	while (*arg) {
+		if (parse_one_token(&arg, "none"))
+			val = 0;
+		else if (parse_one_token(&arg, "default"))
+			val = WSEH_NEW;
+		else if (parse_one_token(&arg, "all"))
+			val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
+		else if (parse_one_token(&arg, "new"))
+			val |= WSEH_NEW;
+		else if (parse_one_token(&arg, "old"))
+			val |= WSEH_OLD;
+		else if (parse_one_token(&arg, "context"))
+			val |= WSEH_CONTEXT;
+		else {
+			error("unknown value after ws-error-highlight=%.*s",
+			      (int)(arg - orig_arg), orig_arg);
+			return 0;
+		}
+		if (*arg)
+			arg++;
+	}
+	opt->ws_error_highlight = val;
+	return 1;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
 	const char *arg = av[0];
@@ -3806,6 +3882,8 @@
 		DIFF_OPT_SET(options, SUBMODULE_LOG);
 	else if (skip_prefix(arg, "--submodule=", &arg))
 		return parse_submodule_opt(options, arg);
+	else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
+		return parse_ws_error_highlight(options, arg);
 
 	/* misc options */
 	else if (!strcmp(arg, "-z"))
diff --git a/diff.h b/diff.h
index 1ac0582..c7ad42a 100644
--- a/diff.h
+++ b/diff.h
@@ -6,6 +6,7 @@
 
 #include "tree-walk.h"
 #include "pathspec.h"
+#include "object.h"
 
 struct rev_info;
 struct diff_options;
@@ -137,6 +138,11 @@
 	int dirstat_permille;
 	int setup;
 	int abbrev;
+/* white-space error highlighting */
+#define WSEH_NEW 1
+#define WSEH_CONTEXT 2
+#define WSEH_OLD 4
+	unsigned ws_error_highlight;
 	const char *prefix;
 	int prefix_length;
 	const char *stat_sep;
@@ -207,11 +213,11 @@
 	struct combine_diff_path *next;
 	char *path;
 	unsigned int mode;
-	unsigned char sha1[20];
+	struct object_id oid;
 	struct combine_diff_parent {
 		char status;
 		unsigned int mode;
-		unsigned char sha1[20];
+		struct object_id oid;
 	} parent[FLEX_ARRAY];
 };
 #define combine_diff_path_size(n, l) \
diff --git a/dir.c b/dir.c
index 56106c9..8209f8b 100644
--- a/dir.c
+++ b/dir.c
@@ -13,6 +13,8 @@
 #include "wildmatch.h"
 #include "pathspec.h"
 #include "utf8.h"
+#include "varint.h"
+#include "ewah/ewok.h"
 
 struct path_simplify {
 	int len;
@@ -32,8 +34,22 @@
 	path_untracked
 };
 
+/*
+ * Support data structure for our opendir/readdir/closedir wrappers
+ */
+struct cached_dir {
+	DIR *fdir;
+	struct untracked_cache_dir *untracked;
+	int nr_files;
+	int nr_dirs;
+
+	struct dirent *de;
+	const char *file;
+	struct untracked_cache_dir *ucd;
+};
+
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
-	const char *path, int len,
+	const char *path, int len, struct untracked_cache_dir *untracked,
 	int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
@@ -508,7 +524,8 @@
 	x->el = el;
 }
 
-static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
+						struct sha1_stat *sha1_stat)
 {
 	int pos, len;
 	unsigned long sz;
@@ -527,6 +544,10 @@
 		return NULL;
 	}
 	*size = xsize_t(sz);
+	if (sha1_stat) {
+		memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
+		hashcpy(sha1_stat->sha1, active_cache[pos]->sha1);
+	}
 	return data;
 }
 
@@ -571,11 +592,93 @@
 		*last_space = '\0';
 }
 
-int add_excludes_from_file_to_list(const char *fname,
-				   const char *base,
-				   int baselen,
-				   struct exclude_list *el,
-				   int check_index)
+/*
+ * Given a subdirectory name and "dir" of the current directory,
+ * search the subdir in "dir" and return it, or create a new one if it
+ * does not exist in "dir".
+ *
+ * If "name" has the trailing slash, it'll be excluded in the search.
+ */
+static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
+						    struct untracked_cache_dir *dir,
+						    const char *name, int len)
+{
+	int first, last;
+	struct untracked_cache_dir *d;
+	if (!dir)
+		return NULL;
+	if (len && name[len - 1] == '/')
+		len--;
+	first = 0;
+	last = dir->dirs_nr;
+	while (last > first) {
+		int cmp, next = (last + first) >> 1;
+		d = dir->dirs[next];
+		cmp = strncmp(name, d->name, len);
+		if (!cmp && strlen(d->name) > len)
+			cmp = -1;
+		if (!cmp)
+			return d;
+		if (cmp < 0) {
+			last = next;
+			continue;
+		}
+		first = next+1;
+	}
+
+	uc->dir_created++;
+	d = xmalloc(sizeof(*d) + len + 1);
+	memset(d, 0, sizeof(*d));
+	memcpy(d->name, name, len);
+	d->name[len] = '\0';
+
+	ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
+	memmove(dir->dirs + first + 1, dir->dirs + first,
+		(dir->dirs_nr - first) * sizeof(*dir->dirs));
+	dir->dirs_nr++;
+	dir->dirs[first] = d;
+	return d;
+}
+
+static void do_invalidate_gitignore(struct untracked_cache_dir *dir)
+{
+	int i;
+	dir->valid = 0;
+	dir->untracked_nr = 0;
+	for (i = 0; i < dir->dirs_nr; i++)
+		do_invalidate_gitignore(dir->dirs[i]);
+}
+
+static void invalidate_gitignore(struct untracked_cache *uc,
+				 struct untracked_cache_dir *dir)
+{
+	uc->gitignore_invalidated++;
+	do_invalidate_gitignore(dir);
+}
+
+static void invalidate_directory(struct untracked_cache *uc,
+				 struct untracked_cache_dir *dir)
+{
+	int i;
+	uc->dir_invalidated++;
+	dir->valid = 0;
+	dir->untracked_nr = 0;
+	for (i = 0; i < dir->dirs_nr; i++)
+		dir->dirs[i]->recurse = 0;
+}
+
+/*
+ * Given a file with name "fname", read it (either from disk, or from
+ * the index if "check_index" is non-zero), parse it and store the
+ * exclude rules in "el".
+ *
+ * If "ss" is not NULL, compute SHA-1 of the exclude file and fill
+ * stat data from disk (only valid if add_excludes returns zero). If
+ * ss_valid is non-zero, "ss" must contain good value as input.
+ */
+static int add_excludes(const char *fname, const char *base, int baselen,
+			struct exclude_list *el, int check_index,
+			struct sha1_stat *sha1_stat)
 {
 	struct stat st;
 	int fd, i, lineno = 1;
@@ -589,7 +692,7 @@
 		if (0 <= fd)
 			close(fd);
 		if (!check_index ||
-		    (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+		    (buf = read_skip_worktree_file_from_index(fname, &size, sha1_stat)) == NULL)
 			return -1;
 		if (size == 0) {
 			free(buf);
@@ -602,6 +705,11 @@
 	} else {
 		size = xsize_t(st.st_size);
 		if (size == 0) {
+			if (sha1_stat) {
+				fill_stat_data(&sha1_stat->stat, &st);
+				hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
+				sha1_stat->valid = 1;
+			}
 			close(fd);
 			return 0;
 		}
@@ -613,6 +721,22 @@
 		}
 		buf[size++] = '\n';
 		close(fd);
+		if (sha1_stat) {
+			int pos;
+			if (sha1_stat->valid &&
+			    !match_stat_data_racy(&the_index, &sha1_stat->stat, &st))
+				; /* no content change, ss->sha1 still good */
+			else if (check_index &&
+				 (pos = cache_name_pos(fname, strlen(fname))) >= 0 &&
+				 !ce_stage(active_cache[pos]) &&
+				 ce_uptodate(active_cache[pos]) &&
+				 !would_convert_to_git(fname))
+				hashcpy(sha1_stat->sha1, active_cache[pos]->sha1);
+			else
+				hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
+			fill_stat_data(&sha1_stat->stat, &st);
+			sha1_stat->valid = 1;
+		}
 	}
 
 	el->filebuf = buf;
@@ -636,6 +760,13 @@
 	return 0;
 }
 
+int add_excludes_from_file_to_list(const char *fname, const char *base,
+				   int baselen, struct exclude_list *el,
+				   int check_index)
+{
+	return add_excludes(fname, base, baselen, el, check_index, NULL);
+}
+
 struct exclude_list *add_exclude_list(struct dir_struct *dir,
 				      int group_type, const char *src)
 {
@@ -653,14 +784,28 @@
 /*
  * Used to set up core.excludesfile and .git/info/exclude lists.
  */
-void add_excludes_from_file(struct dir_struct *dir, const char *fname)
+static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
+				     struct sha1_stat *sha1_stat)
 {
 	struct exclude_list *el;
+	/*
+	 * catch setup_standard_excludes() that's called before
+	 * dir->untracked is assigned. That function behaves
+	 * differently when dir->untracked is non-NULL.
+	 */
+	if (!dir->untracked)
+		dir->unmanaged_exclude_files++;
 	el = add_exclude_list(dir, EXC_FILE, fname);
-	if (add_excludes_from_file_to_list(fname, "", 0, el, 0) < 0)
+	if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
 		die("cannot use %s as an exclude file", fname);
 }
 
+void add_excludes_from_file(struct dir_struct *dir, const char *fname)
+{
+	dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
+	add_excludes_from_file_1(dir, fname, NULL);
+}
+
 int match_basename(const char *basename, int basenamelen,
 		   const char *pattern, int prefix, int patternlen,
 		   int flags)
@@ -835,6 +980,7 @@
 	struct exclude_list_group *group;
 	struct exclude_list *el;
 	struct exclude_stack *stk = NULL;
+	struct untracked_cache_dir *untracked;
 	int current;
 
 	group = &dir->exclude_list_group[EXC_DIRS];
@@ -872,8 +1018,14 @@
 	/* Read from the parent directories and push them down. */
 	current = stk ? stk->baselen : -1;
 	strbuf_setlen(&dir->basebuf, current < 0 ? 0 : current);
+	if (dir->untracked)
+		untracked = stk ? stk->ucd : dir->untracked->root;
+	else
+		untracked = NULL;
+
 	while (current < baselen) {
 		const char *cp;
+		struct sha1_stat sha1_stat;
 
 		stk = xcalloc(1, sizeof(*stk));
 		if (current < 0) {
@@ -884,10 +1036,15 @@
 			if (!cp)
 				die("oops in prep_exclude");
 			cp++;
+			untracked =
+				lookup_untracked(dir->untracked, untracked,
+						 base + current,
+						 cp - base - current);
 		}
 		stk->prev = dir->exclude_stack;
 		stk->baselen = cp - base;
 		stk->exclude_ix = group->nr;
+		stk->ucd = untracked;
 		el = add_exclude_list(dir, EXC_DIRS, NULL);
 		strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
 		assert(stk->baselen == dir->basebuf.len);
@@ -910,7 +1067,23 @@
 		}
 
 		/* Try to read per-directory file */
-		if (dir->exclude_per_dir) {
+		hashclr(sha1_stat.sha1);
+		sha1_stat.valid = 0;
+		if (dir->exclude_per_dir &&
+		    /*
+		     * If we know that no files have been added in
+		     * this directory (i.e. valid_cached_dir() has
+		     * been executed and set untracked->valid) ..
+		     */
+		    (!untracked || !untracked->valid ||
+		     /*
+		      * .. and .gitignore does not exist before
+		      * (i.e. null exclude_sha1 and skip_worktree is
+		      * not set). Then we can skip loading .gitignore,
+		      * which would result in ENOENT anyway.
+		      * skip_worktree is taken care in read_directory()
+		      */
+		     !is_null_sha1(untracked->exclude_sha1))) {
 			/*
 			 * dir->basebuf gets reused by the traversal, but we
 			 * need fname to remain unchanged to ensure the src
@@ -923,8 +1096,27 @@
 			strbuf_addbuf(&sb, &dir->basebuf);
 			strbuf_addstr(&sb, dir->exclude_per_dir);
 			el->src = strbuf_detach(&sb, NULL);
-			add_excludes_from_file_to_list(el->src, el->src,
-						       stk->baselen, el, 1);
+			add_excludes(el->src, el->src, stk->baselen, el, 1,
+				     untracked ? &sha1_stat : NULL);
+		}
+		/*
+		 * NEEDSWORK: when untracked cache is enabled, prep_exclude()
+		 * will first be called in valid_cached_dir() then maybe many
+		 * times more in last_exclude_matching(). When the cache is
+		 * used, last_exclude_matching() will not be called and
+		 * reading .gitignore content will be a waste.
+		 *
+		 * So when it's called by valid_cached_dir() and we can get
+		 * .gitignore SHA-1 from the index (i.e. .gitignore is not
+		 * modified on work tree), we could delay reading the
+		 * .gitignore content until we absolutely need it in
+		 * last_exclude_matching(). Be careful about ignore rule
+		 * order, though, if you do that.
+		 */
+		if (untracked &&
+		    hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
+			invalidate_gitignore(dir->untracked, untracked);
+			hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
 		}
 		dir->exclude_stack = stk;
 		current = stk->baselen;
@@ -1105,6 +1297,7 @@
  *  (c) otherwise, we recurse into it.
  */
 static enum path_treatment treat_directory(struct dir_struct *dir,
+	struct untracked_cache_dir *untracked,
 	const char *dirname, int len, int exclude,
 	const struct path_simplify *simplify)
 {
@@ -1132,7 +1325,9 @@
 	if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
 		return exclude ? path_excluded : path_untracked;
 
-	return read_directory_recursive(dir, dirname, len, 1, simplify);
+	untracked = lookup_untracked(dir->untracked, untracked, dirname, len);
+	return read_directory_recursive(dir, dirname, len,
+					untracked, 1, simplify);
 }
 
 /*
@@ -1248,6 +1443,7 @@
 }
 
 static enum path_treatment treat_one_path(struct dir_struct *dir,
+					  struct untracked_cache_dir *untracked,
 					  struct strbuf *path,
 					  const struct path_simplify *simplify,
 					  int dtype, struct dirent *de)
@@ -1300,7 +1496,7 @@
 		return path_none;
 	case DT_DIR:
 		strbuf_addch(path, '/');
-		return treat_directory(dir, path->buf, path->len, exclude,
+		return treat_directory(dir, untracked, path->buf, path->len, exclude,
 			simplify);
 	case DT_REG:
 	case DT_LNK:
@@ -1308,14 +1504,52 @@
 	}
 }
 
+static enum path_treatment treat_path_fast(struct dir_struct *dir,
+					   struct untracked_cache_dir *untracked,
+					   struct cached_dir *cdir,
+					   struct strbuf *path,
+					   int baselen,
+					   const struct path_simplify *simplify)
+{
+	strbuf_setlen(path, baselen);
+	if (!cdir->ucd) {
+		strbuf_addstr(path, cdir->file);
+		return path_untracked;
+	}
+	strbuf_addstr(path, cdir->ucd->name);
+	/* treat_one_path() does this before it calls treat_directory() */
+	if (path->buf[path->len - 1] != '/')
+		strbuf_addch(path, '/');
+	if (cdir->ucd->check_only)
+		/*
+		 * check_only is set as a result of treat_directory() getting
+		 * to its bottom. Verify again the same set of directories
+		 * with check_only set.
+		 */
+		return read_directory_recursive(dir, path->buf, path->len,
+						cdir->ucd, 1, simplify);
+	/*
+	 * We get path_recurse in the first run when
+	 * directory_exists_in_index() returns index_nonexistent. We
+	 * are sure that new changes in the index does not impact the
+	 * outcome. Return now.
+	 */
+	return path_recurse;
+}
+
 static enum path_treatment treat_path(struct dir_struct *dir,
-				      struct dirent *de,
+				      struct untracked_cache_dir *untracked,
+				      struct cached_dir *cdir,
 				      struct strbuf *path,
 				      int baselen,
 				      const struct path_simplify *simplify)
 {
 	int dtype;
+	struct dirent *de = cdir->de;
 
+	if (!de)
+		return treat_path_fast(dir, untracked, cdir, path,
+				       baselen, simplify);
 	if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
 		return path_none;
 	strbuf_setlen(path, baselen);
@@ -1324,7 +1558,121 @@
 		return path_none;
 
 	dtype = DTYPE(de);
-	return treat_one_path(dir, path, simplify, dtype, de);
+	return treat_one_path(dir, untracked, path, simplify, dtype, de);
+}
+
+static void add_untracked(struct untracked_cache_dir *dir, const char *name)
+{
+	if (!dir)
+		return;
+	ALLOC_GROW(dir->untracked, dir->untracked_nr + 1,
+		   dir->untracked_alloc);
+	dir->untracked[dir->untracked_nr++] = xstrdup(name);
+}
+
+static int valid_cached_dir(struct dir_struct *dir,
+			    struct untracked_cache_dir *untracked,
+			    struct strbuf *path,
+			    int check_only)
+{
+	struct stat st;
+
+	if (!untracked)
+		return 0;
+
+	if (stat(path->len ? path->buf : ".", &st)) {
+		invalidate_directory(dir->untracked, untracked);
+		memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
+		return 0;
+	}
+	if (!untracked->valid ||
+	    match_stat_data_racy(&the_index, &untracked->stat_data, &st)) {
+		if (untracked->valid)
+			invalidate_directory(dir->untracked, untracked);
+		fill_stat_data(&untracked->stat_data, &st);
+		return 0;
+	}
+
+	if (untracked->check_only != !!check_only) {
+		invalidate_directory(dir->untracked, untracked);
+		return 0;
+	}
+
+	/*
+	 * prep_exclude will be called eventually on this directory,
+	 * but it's called much later in last_exclude_matching(). We
+	 * need it now to determine the validity of the cache for this
+	 * path. The next calls will be nearly no-op, the way
+	 * prep_exclude() is designed.
+	 */
+	if (path->len && path->buf[path->len - 1] != '/') {
+		strbuf_addch(path, '/');
+		prep_exclude(dir, path->buf, path->len);
+		strbuf_setlen(path, path->len - 1);
+	} else
+		prep_exclude(dir, path->buf, path->len);
+
+	/* hopefully prep_exclude() haven't invalidated this entry... */
+	return untracked->valid;
+}
+
+static int open_cached_dir(struct cached_dir *cdir,
+			   struct dir_struct *dir,
+			   struct untracked_cache_dir *untracked,
+			   struct strbuf *path,
+			   int check_only)
+{
+	memset(cdir, 0, sizeof(*cdir));
+	cdir->untracked = untracked;
+	if (valid_cached_dir(dir, untracked, path, check_only))
+		return 0;
+	cdir->fdir = opendir(path->len ? path->buf : ".");
+	if (dir->untracked)
+		dir->untracked->dir_opened++;
+	if (!cdir->fdir)
+		return -1;
+	return 0;
+}
+
+static int read_cached_dir(struct cached_dir *cdir)
+{
+	if (cdir->fdir) {
+		cdir->de = readdir(cdir->fdir);
+		if (!cdir->de)
+			return -1;
+		return 0;
+	}
+	while (cdir->nr_dirs < cdir->untracked->dirs_nr) {
+		struct untracked_cache_dir *d = cdir->untracked->dirs[cdir->nr_dirs];
+		if (!d->recurse) {
+			cdir->nr_dirs++;
+			continue;
+		}
+		cdir->ucd = d;
+		cdir->nr_dirs++;
+		return 0;
+	}
+	cdir->ucd = NULL;
+	if (cdir->nr_files < cdir->untracked->untracked_nr) {
+		struct untracked_cache_dir *d = cdir->untracked;
+		cdir->file = d->untracked[cdir->nr_files++];
+		return 0;
+	}
+	return -1;
+}
+
+static void close_cached_dir(struct cached_dir *cdir)
+{
+	if (cdir->fdir)
+		closedir(cdir->fdir);
+	/*
+	 * We have gone through this directory and found no untracked
+	 * entries. Mark it valid.
+	 */
+	if (cdir->untracked) {
+		cdir->untracked->valid = 1;
+		cdir->untracked->recurse = 1;
+	}
 }
 
 /*
@@ -1340,38 +1688,48 @@
  */
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
 				    const char *base, int baselen,
-				    int check_only,
+				    struct untracked_cache_dir *untracked, int check_only,
 				    const struct path_simplify *simplify)
 {
-	DIR *fdir;
+	struct cached_dir cdir;
 	enum path_treatment state, subdir_state, dir_state = path_none;
-	struct dirent *de;
 	struct strbuf path = STRBUF_INIT;
 
 	strbuf_add(&path, base, baselen);
 
-	fdir = opendir(path.len ? path.buf : ".");
-	if (!fdir)
+	if (open_cached_dir(&cdir, dir, untracked, &path, check_only))
 		goto out;
 
-	while ((de = readdir(fdir)) != NULL) {
+	if (untracked)
+		untracked->check_only = !!check_only;
+
+	while (!read_cached_dir(&cdir)) {
 		/* check how the file or directory should be treated */
-		state = treat_path(dir, de, &path, baselen, simplify);
+		state = treat_path(dir, untracked, &cdir, &path, baselen, simplify);
+
 		if (state > dir_state)
 			dir_state = state;
 
 		/* recurse into subdir if instructed by treat_path */
 		if (state == path_recurse) {
-			subdir_state = read_directory_recursive(dir, path.buf,
-				path.len, check_only, simplify);
+			struct untracked_cache_dir *ud;
+			ud = lookup_untracked(dir->untracked, untracked,
+					      path.buf + baselen,
+					      path.len - baselen);
+			subdir_state =
+				read_directory_recursive(dir, path.buf, path.len,
+							 ud, check_only, simplify);
 			if (subdir_state > dir_state)
 				dir_state = subdir_state;
 		}
 
 		if (check_only) {
 			/* abort early if maximum state has been reached */
-			if (dir_state == path_untracked)
+			if (dir_state == path_untracked) {
+				if (cdir.fdir)
+					add_untracked(untracked, path.buf + baselen);
 				break;
+			}
 			/* skip the dir_add_* part */
 			continue;
 		}
@@ -1389,15 +1747,18 @@
 			break;
 
 		case path_untracked:
-			if (!(dir->flags & DIR_SHOW_IGNORED))
-				dir_add_name(dir, path.buf, path.len);
+			if (dir->flags & DIR_SHOW_IGNORED)
+				break;
+			dir_add_name(dir, path.buf, path.len);
+			if (cdir.fdir)
+				add_untracked(untracked, path.buf + baselen);
 			break;
 
 		default:
 			break;
 		}
 	}
-	closedir(fdir);
+	close_cached_dir(&cdir);
  out:
 	strbuf_release(&path);
 
@@ -1467,7 +1828,7 @@
 			break;
 		if (simplify_away(sb.buf, sb.len, simplify))
 			break;
-		if (treat_one_path(dir, &sb, simplify,
+		if (treat_one_path(dir, NULL, &sb, simplify,
 				   DT_DIR, NULL) == path_none)
 			break; /* do not recurse into it */
 		if (len <= baselen) {
@@ -1480,9 +1841,139 @@
 	return rc;
 }
 
+static const char *get_ident_string(void)
+{
+	static struct strbuf sb = STRBUF_INIT;
+	struct utsname uts;
+
+	if (sb.len)
+		return sb.buf;
+	if (uname(&uts))
+		die_errno(_("failed to get kernel name and information"));
+	strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(),
+		    uts.sysname, uts.release, uts.version);
+	return sb.buf;
+}
+
+static int ident_in_untracked(const struct untracked_cache *uc)
+{
+	const char *end = uc->ident.buf + uc->ident.len;
+	const char *p   = uc->ident.buf;
+
+	for (p = uc->ident.buf; p < end; p += strlen(p) + 1)
+		if (!strcmp(p, get_ident_string()))
+			return 1;
+	return 0;
+}
+
+void add_untracked_ident(struct untracked_cache *uc)
+{
+	if (ident_in_untracked(uc))
+		return;
+	strbuf_addstr(&uc->ident, get_ident_string());
+	/* this strbuf contains a list of strings, save NUL too */
+	strbuf_addch(&uc->ident, 0);
+}
+
+static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
+						      int base_len,
+						      const struct pathspec *pathspec)
+{
+	struct untracked_cache_dir *root;
+	int i;
+
+	if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
+		return NULL;
+
+	/*
+	 * We only support $GIT_DIR/info/exclude and core.excludesfile
+	 * as the global ignore rule files. Any other additions
+	 * (e.g. from command line) invalidate the cache. This
+	 * condition also catches running setup_standard_excludes()
+	 * before setting dir->untracked!
+	 */
+	if (dir->unmanaged_exclude_files)
+		return NULL;
+
+	/*
+	 * Optimize for the main use case only: whole-tree git
+	 * status. More work involved in treat_leading_path() if we
+	 * use cache on just a subset of the worktree. pathspec
+	 * support could make the matter even worse.
+	 */
+	if (base_len || (pathspec && pathspec->nr))
+		return NULL;
+
+	/* Different set of flags may produce different results */
+	if (dir->flags != dir->untracked->dir_flags ||
+	    /*
+	     * See treat_directory(), case index_nonexistent. Without
+	     * this flag, we may need to also cache .git file content
+	     * for the resolve_gitlink_ref() call, which we don't.
+	     */
+	    !(dir->flags & DIR_SHOW_OTHER_DIRECTORIES) ||
+	    /* We don't support collecting ignore files */
+	    (dir->flags & (DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO |
+			   DIR_COLLECT_IGNORED)))
+		return NULL;
+
+	/*
+	 * If we use .gitignore in the cache and now you change it to
+	 * .gitexclude, everything will go wrong.
+	 */
+	if (dir->exclude_per_dir != dir->untracked->exclude_per_dir &&
+	    strcmp(dir->exclude_per_dir, dir->untracked->exclude_per_dir))
+		return NULL;
+
+	/*
+	 * EXC_CMDL is not considered in the cache. If people set it,
+	 * skip the cache.
+	 */
+	if (dir->exclude_list_group[EXC_CMDL].nr)
+		return NULL;
+
+	/*
+	 * An optimization in prep_exclude() does not play well with
+	 * CE_SKIP_WORKTREE. It's a rare case anyway, if a single
+	 * entry has that bit set, disable the whole untracked cache.
+	 */
+	for (i = 0; i < active_nr; i++)
+		if (ce_skip_worktree(active_cache[i]))
+			return NULL;
+
+	if (!ident_in_untracked(dir->untracked)) {
+		warning(_("Untracked cache is disabled on this system."));
+		return NULL;
+	}
+
+	if (!dir->untracked->root) {
+		const int len = sizeof(*dir->untracked->root);
+		dir->untracked->root = xmalloc(len);
+		memset(dir->untracked->root, 0, len);
+	}
+
+	/* Validate $GIT_DIR/info/exclude and core.excludesfile */
+	root = dir->untracked->root;
+	if (hashcmp(dir->ss_info_exclude.sha1,
+		    dir->untracked->ss_info_exclude.sha1)) {
+		invalidate_gitignore(dir->untracked, root);
+		dir->untracked->ss_info_exclude = dir->ss_info_exclude;
+	}
+	if (hashcmp(dir->ss_excludes_file.sha1,
+		    dir->untracked->ss_excludes_file.sha1)) {
+		invalidate_gitignore(dir->untracked, root);
+		dir->untracked->ss_excludes_file = dir->ss_excludes_file;
+	}
+
+	/* Make sure this directory is not dropped out at saving phase */
+	root->recurse = 1;
+	return root;
+}
+
 int read_directory(struct dir_struct *dir, const char *path, int len, const struct pathspec *pathspec)
 {
 	struct path_simplify *simplify;
+	struct untracked_cache_dir *untracked;
 
 	/*
 	 * Check out create_simplify()
@@ -1506,11 +1997,39 @@
 	 * create_simplify().
 	 */
 	simplify = create_simplify(pathspec ? pathspec->_raw : NULL);
+	untracked = validate_untracked_cache(dir, len, pathspec);
+	if (!untracked)
+		/*
+		 * make sure untracked cache code path is disabled,
+		 * e.g. prep_exclude()
+		 */
+		dir->untracked = NULL;
 	if (!len || treat_leading_path(dir, path, len, simplify))
-		read_directory_recursive(dir, path, len, 0, simplify);
+		read_directory_recursive(dir, path, len, untracked, 0, simplify);
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
 	qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
+	if (dir->untracked) {
+		static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
+		trace_printf_key(&trace_untracked_stats,
+				 "node creation: %u\n"
+				 "gitignore invalidation: %u\n"
+				 "directory invalidation: %u\n"
+				 "opendir: %u\n",
+				 dir->untracked->dir_created,
+				 dir->untracked->gitignore_invalidated,
+				 dir->untracked->dir_invalidated,
+				 dir->untracked->dir_opened);
+		if (dir->untracked == the_index.untracked &&
+		    (dir->untracked->dir_opened ||
+		     dir->untracked->gitignore_invalidated ||
+		     dir->untracked->dir_invalidated))
+			the_index.cache_changed |= UNTRACKED_CHANGED;
+		if (dir->untracked != the_index.untracked) {
+			free(dir->untracked);
+			dir->untracked = NULL;
+		}
+	}
 	return dir->nr;
 }
 
@@ -1676,12 +2195,14 @@
 	if (!excludes_file)
 		excludes_file = xdg_config_home("ignore");
 	if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
-		add_excludes_from_file(dir, excludes_file);
+		add_excludes_from_file_1(dir, excludes_file,
+					 dir->untracked ? &dir->ss_excludes_file : NULL);
 
 	/* per repository user preference */
 	path = git_path("info/exclude");
 	if (!access_or_warn(path, R_OK, 0))
-		add_excludes_from_file(dir, path);
+		add_excludes_from_file_1(dir, path,
+					 dir->untracked ? &dir->ss_info_exclude : NULL);
 }
 
 int remove_path(const char *name)
@@ -1733,3 +2254,404 @@
 	}
 	strbuf_release(&dir->basebuf);
 }
+
+struct ondisk_untracked_cache {
+	struct stat_data info_exclude_stat;
+	struct stat_data excludes_file_stat;
+	uint32_t dir_flags;
+	unsigned char info_exclude_sha1[20];
+	unsigned char excludes_file_sha1[20];
+	char exclude_per_dir[FLEX_ARRAY];
+};
+
+#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
+
+struct write_data {
+	int index;	   /* number of written untracked_cache_dir */
+	struct ewah_bitmap *check_only; /* from untracked_cache_dir */
+	struct ewah_bitmap *valid;	/* from untracked_cache_dir */
+	struct ewah_bitmap *sha1_valid; /* set if exclude_sha1 is not null */
+	struct strbuf out;
+	struct strbuf sb_stat;
+	struct strbuf sb_sha1;
+};
+
+static void stat_data_to_disk(struct stat_data *to, const struct stat_data *from)
+{
+	to->sd_ctime.sec  = htonl(from->sd_ctime.sec);
+	to->sd_ctime.nsec = htonl(from->sd_ctime.nsec);
+	to->sd_mtime.sec  = htonl(from->sd_mtime.sec);
+	to->sd_mtime.nsec = htonl(from->sd_mtime.nsec);
+	to->sd_dev	  = htonl(from->sd_dev);
+	to->sd_ino	  = htonl(from->sd_ino);
+	to->sd_uid	  = htonl(from->sd_uid);
+	to->sd_gid	  = htonl(from->sd_gid);
+	to->sd_size	  = htonl(from->sd_size);
+}
+
+static void write_one_dir(struct untracked_cache_dir *untracked,
+			  struct write_data *wd)
+{
+	struct stat_data stat_data;
+	struct strbuf *out = &wd->out;
+	unsigned char intbuf[16];
+	unsigned int intlen, value;
+	int i = wd->index++;
+
+	/*
+	 * untracked_nr should be reset whenever valid is clear, but
+	 * for safety..
+	 */
+	if (!untracked->valid) {
+		untracked->untracked_nr = 0;
+		untracked->check_only = 0;
+	}
+
+	if (untracked->check_only)
+		ewah_set(wd->check_only, i);
+	if (untracked->valid) {
+		ewah_set(wd->valid, i);
+		stat_data_to_disk(&stat_data, &untracked->stat_data);
+		strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
+	}
+	if (!is_null_sha1(untracked->exclude_sha1)) {
+		ewah_set(wd->sha1_valid, i);
+		strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
+	}
+
+	intlen = encode_varint(untracked->untracked_nr, intbuf);
+	strbuf_add(out, intbuf, intlen);
+
+	/* skip non-recurse directories */
+	for (i = 0, value = 0; i < untracked->dirs_nr; i++)
+		if (untracked->dirs[i]->recurse)
+			value++;
+	intlen = encode_varint(value, intbuf);
+	strbuf_add(out, intbuf, intlen);
+
+	strbuf_add(out, untracked->name, strlen(untracked->name) + 1);
+
+	for (i = 0; i < untracked->untracked_nr; i++)
+		strbuf_add(out, untracked->untracked[i],
+			   strlen(untracked->untracked[i]) + 1);
+
+	for (i = 0; i < untracked->dirs_nr; i++)
+		if (untracked->dirs[i]->recurse)
+			write_one_dir(untracked->dirs[i], wd);
+}
+
+void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked)
+{
+	struct ondisk_untracked_cache *ouc;
+	struct write_data wd;
+	unsigned char varbuf[16];
+	int len = 0, varint_len;
+	if (untracked->exclude_per_dir)
+		len = strlen(untracked->exclude_per_dir);
+	ouc = xmalloc(sizeof(*ouc) + len + 1);
+	stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
+	stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
+	hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
+	hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
+	ouc->dir_flags = htonl(untracked->dir_flags);
+	memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
+
+	varint_len = encode_varint(untracked->ident.len, varbuf);
+	strbuf_add(out, varbuf, varint_len);
+	strbuf_add(out, untracked->ident.buf, untracked->ident.len);
+
+	strbuf_add(out, ouc, ouc_size(len));
+	free(ouc);
+	ouc = NULL;
+
+	if (!untracked->root) {
+		varint_len = encode_varint(0, varbuf);
+		strbuf_add(out, varbuf, varint_len);
+		return;
+	}
+
+	wd.index      = 0;
+	wd.check_only = ewah_new();
+	wd.valid      = ewah_new();
+	wd.sha1_valid = ewah_new();
+	strbuf_init(&wd.out, 1024);
+	strbuf_init(&wd.sb_stat, 1024);
+	strbuf_init(&wd.sb_sha1, 1024);
+	write_one_dir(untracked->root, &wd);
+
+	varint_len = encode_varint(wd.index, varbuf);
+	strbuf_add(out, varbuf, varint_len);
+	strbuf_addbuf(out, &wd.out);
+	ewah_serialize_strbuf(wd.valid, out);
+	ewah_serialize_strbuf(wd.check_only, out);
+	ewah_serialize_strbuf(wd.sha1_valid, out);
+	strbuf_addbuf(out, &wd.sb_stat);
+	strbuf_addbuf(out, &wd.sb_sha1);
+	strbuf_addch(out, '\0'); /* safe guard for string lists */
+
+	ewah_free(wd.valid);
+	ewah_free(wd.check_only);
+	ewah_free(wd.sha1_valid);
+	strbuf_release(&wd.out);
+	strbuf_release(&wd.sb_stat);
+	strbuf_release(&wd.sb_sha1);
+}
+
+static void free_untracked(struct untracked_cache_dir *ucd)
+{
+	int i;
+	if (!ucd)
+		return;
+	for (i = 0; i < ucd->dirs_nr; i++)
+		free_untracked(ucd->dirs[i]);
+	for (i = 0; i < ucd->untracked_nr; i++)
+		free(ucd->untracked[i]);
+	free(ucd->untracked);
+	free(ucd->dirs);
+	free(ucd);
+}
+
+void free_untracked_cache(struct untracked_cache *uc)
+{
+	if (uc)
+		free_untracked(uc->root);
+	free(uc);
+}
+
+struct read_data {
+	int index;
+	struct untracked_cache_dir **ucd;
+	struct ewah_bitmap *check_only;
+	struct ewah_bitmap *valid;
+	struct ewah_bitmap *sha1_valid;
+	const unsigned char *data;
+	const unsigned char *end;
+};
+
+static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+{
+	to->sd_ctime.sec  = get_be32(&from->sd_ctime.sec);
+	to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
+	to->sd_mtime.sec  = get_be32(&from->sd_mtime.sec);
+	to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
+	to->sd_dev	  = get_be32(&from->sd_dev);
+	to->sd_ino	  = get_be32(&from->sd_ino);
+	to->sd_uid	  = get_be32(&from->sd_uid);
+	to->sd_gid	  = get_be32(&from->sd_gid);
+	to->sd_size	  = get_be32(&from->sd_size);
+}
+
+static int read_one_dir(struct untracked_cache_dir **untracked_,
+			struct read_data *rd)
+{
+	struct untracked_cache_dir ud, *untracked;
+	const unsigned char *next, *data = rd->data, *end = rd->end;
+	unsigned int value;
+	int i, len;
+
+	memset(&ud, 0, sizeof(ud));
+
+	next = data;
+	value = decode_varint(&next);
+	if (next > end)
+		return -1;
+	ud.recurse	   = 1;
+	ud.untracked_alloc = value;
+	ud.untracked_nr	   = value;
+	if (ud.untracked_nr)
+		ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+	data = next;
+
+	next = data;
+	ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
+	if (next > end)
+		return -1;
+	ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+	data = next;
+
+	len = strlen((const char *)data);
+	next = data + len + 1;
+	if (next > rd->end)
+		return -1;
+	*untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+	memcpy(untracked, &ud, sizeof(ud));
+	memcpy(untracked->name, data, len + 1);
+	data = next;
+
+	for (i = 0; i < untracked->untracked_nr; i++) {
+		len = strlen((const char *)data);
+		next = data + len + 1;
+		if (next > rd->end)
+			return -1;
+		untracked->untracked[i] = xstrdup((const char*)data);
+		data = next;
+	}
+
+	rd->ucd[rd->index++] = untracked;
+	rd->data = data;
+
+	for (i = 0; i < untracked->dirs_nr; i++) {
+		len = read_one_dir(untracked->dirs + i, rd);
+		if (len < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static void set_check_only(size_t pos, void *cb)
+{
+	struct read_data *rd = cb;
+	struct untracked_cache_dir *ud = rd->ucd[pos];
+	ud->check_only = 1;
+}
+
+static void read_stat(size_t pos, void *cb)
+{
+	struct read_data *rd = cb;
+	struct untracked_cache_dir *ud = rd->ucd[pos];
+	if (rd->data + sizeof(struct stat_data) > rd->end) {
+		rd->data = rd->end + 1;
+		return;
+	}
+	stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+	rd->data += sizeof(struct stat_data);
+	ud->valid = 1;
+}
+
+static void read_sha1(size_t pos, void *cb)
+{
+	struct read_data *rd = cb;
+	struct untracked_cache_dir *ud = rd->ucd[pos];
+	if (rd->data + 20 > rd->end) {
+		rd->data = rd->end + 1;
+		return;
+	}
+	hashcpy(ud->exclude_sha1, rd->data);
+	rd->data += 20;
+}
+
+static void load_sha1_stat(struct sha1_stat *sha1_stat,
+			   const struct stat_data *stat,
+			   const unsigned char *sha1)
+{
+	stat_data_from_disk(&sha1_stat->stat, stat);
+	hashcpy(sha1_stat->sha1, sha1);
+	sha1_stat->valid = 1;
+}
+
+struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
+{
+	const struct ondisk_untracked_cache *ouc;
+	struct untracked_cache *uc;
+	struct read_data rd;
+	const unsigned char *next = data, *end = (const unsigned char *)data + sz;
+	const char *ident;
+	int ident_len, len;
+
+	if (sz <= 1 || end[-1] != '\0')
+		return NULL;
+	end--;
+
+	ident_len = decode_varint(&next);
+	if (next + ident_len > end)
+		return NULL;
+	ident = (const char *)next;
+	next += ident_len;
+
+	ouc = (const struct ondisk_untracked_cache *)next;
+	if (next + ouc_size(0) > end)
+		return NULL;
+
+	uc = xcalloc(1, sizeof(*uc));
+	strbuf_init(&uc->ident, ident_len);
+	strbuf_add(&uc->ident, ident, ident_len);
+	load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
+		       ouc->info_exclude_sha1);
+	load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
+		       ouc->excludes_file_sha1);
+	uc->dir_flags = get_be32(&ouc->dir_flags);
+	uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+	/* NUL after exclude_per_dir is covered by sizeof(*ouc) */
+	next += ouc_size(strlen(ouc->exclude_per_dir));
+	if (next >= end)
+		goto done2;
+
+	len = decode_varint(&next);
+	if (next > end || len == 0)
+		goto done2;
+
+	rd.valid      = ewah_new();
+	rd.check_only = ewah_new();
+	rd.sha1_valid = ewah_new();
+	rd.data	      = next;
+	rd.end	      = end;
+	rd.index      = 0;
+	rd.ucd        = xmalloc(sizeof(*rd.ucd) * len);
+
+	if (read_one_dir(&uc->root, &rd) || rd.index != len)
+		goto done;
+
+	next = rd.data;
+	len = ewah_read_mmap(rd.valid, next, end - next);
+	if (len < 0)
+		goto done;
+
+	next += len;
+	len = ewah_read_mmap(rd.check_only, next, end - next);
+	if (len < 0)
+		goto done;
+
+	next += len;
+	len = ewah_read_mmap(rd.sha1_valid, next, end - next);
+	if (len < 0)
+		goto done;
+
+	ewah_each_bit(rd.check_only, set_check_only, &rd);
+	rd.data = next + len;
+	ewah_each_bit(rd.valid, read_stat, &rd);
+	ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
+	next = rd.data;
+
+done:
+	free(rd.ucd);
+	ewah_free(rd.valid);
+	ewah_free(rd.check_only);
+	ewah_free(rd.sha1_valid);
+done2:
+	if (next != end) {
+		free_untracked_cache(uc);
+		uc = NULL;
+	}
+	return uc;
+}
+
+void untracked_cache_invalidate_path(struct index_state *istate,
+				     const char *path)
+{
+	const char *sep;
+	struct untracked_cache_dir *d;
+	if (!istate->untracked || !istate->untracked->root)
+		return;
+	sep = strrchr(path, '/');
+	if (sep)
+		d = lookup_untracked(istate->untracked,
+				     istate->untracked->root,
+				     path, sep - path);
+	else
+		d = istate->untracked->root;
+	istate->untracked->dir_invalidated++;
+	d->valid = 0;
+	d->untracked_nr = 0;
+}
+
+void untracked_cache_remove_from_index(struct index_state *istate,
+				       const char *path)
+{
+	untracked_cache_invalidate_path(istate, path);
+}
+
+void untracked_cache_add_to_index(struct index_state *istate,
+				  const char *path)
+{
+	untracked_cache_invalidate_path(istate, path);
+}
diff --git a/dir.h b/dir.h
index 72b73c6..7b5855d 100644
--- a/dir.h
+++ b/dir.h
@@ -66,6 +66,7 @@
 	struct exclude_stack *prev; /* the struct exclude_stack for the parent directory */
 	int baselen;
 	int exclude_ix; /* index of exclude_list within EXC_DIRS exclude_list_group */
+	struct untracked_cache_dir *ucd;
 };
 
 struct exclude_list_group {
@@ -73,6 +74,73 @@
 	struct exclude_list *el;
 };
 
+struct sha1_stat {
+	struct stat_data stat;
+	unsigned char sha1[20];
+	int valid;
+};
+
+/*
+ *  Untracked cache
+ *
+ *  The following inputs are sufficient to determine what files in a
+ *  directory are excluded:
+ *
+ *   - The list of files and directories of the directory in question
+ *   - The $GIT_DIR/index
+ *   - dir_struct flags
+ *   - The content of $GIT_DIR/info/exclude
+ *   - The content of core.excludesfile
+ *   - The content (or the lack) of .gitignore of all parent directories
+ *     from $GIT_WORK_TREE
+ *   - The check_only flag in read_directory_recursive (for
+ *     DIR_HIDE_EMPTY_DIRECTORIES)
+ *
+ *  The first input can be checked using directory mtime. In many
+ *  filesystems, directory mtime (stat_data field) is updated when its
+ *  files or direct subdirs are added or removed.
+ *
+ *  The second one can be hooked from cache_tree_invalidate_path().
+ *  Whenever a file (or a submodule) is added or removed from a
+ *  directory, we invalidate that directory.
+ *
+ *  The remaining inputs are easy, their SHA-1 could be used to verify
+ *  their contents (exclude_sha1[], info_exclude_sha1[] and
+ *  excludes_file_sha1[])
+ */
+struct untracked_cache_dir {
+	struct untracked_cache_dir **dirs;
+	char **untracked;
+	struct stat_data stat_data;
+	unsigned int untracked_alloc, dirs_nr, dirs_alloc;
+	unsigned int untracked_nr;
+	unsigned int check_only : 1;
+	/* all data except 'dirs' in this struct are good */
+	unsigned int valid : 1;
+	unsigned int recurse : 1;
+	/* null SHA-1 means this directory does not have .gitignore */
+	unsigned char exclude_sha1[20];
+	char name[FLEX_ARRAY];
+};
+
+struct untracked_cache {
+	struct sha1_stat ss_info_exclude;
+	struct sha1_stat ss_excludes_file;
+	const char *exclude_per_dir;
+	struct strbuf ident;
+	/*
+	 * dir_struct#flags must match dir_flags or the untracked
+	 * cache is ignored.
+	 */
+	unsigned dir_flags;
+	struct untracked_cache_dir *root;
+	/* Statistics */
+	int dir_created;
+	int gitignore_invalidated;
+	int dir_invalidated;
+	int dir_opened;
+};
+
 struct dir_struct {
 	int nr, alloc;
 	int ignored_nr, ignored_alloc;
@@ -120,6 +188,12 @@
 	struct exclude_stack *exclude_stack;
 	struct exclude *exclude;
 	struct strbuf basebuf;
+
+	/* Enable untracked file cache if set */
+	struct untracked_cache *untracked;
+	struct sha1_stat ss_info_exclude;
+	struct sha1_stat ss_excludes_file;
+	unsigned unmanaged_exclude_files;
 };
 
 /*
@@ -226,4 +300,12 @@
 			      has_trailing_dir);
 }
 
+void untracked_cache_invalidate_path(struct index_state *, const char *);
+void untracked_cache_remove_from_index(struct index_state *, const char *);
+void untracked_cache_add_to_index(struct index_state *, const char *);
+
+void free_untracked_cache(struct untracked_cache *);
+struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz);
+void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
+void add_untracked_ident(struct untracked_cache *);
 #endif
diff --git a/environment.c b/environment.c
index a40044c..61c685b 100644
--- a/environment.c
+++ b/environment.c
@@ -92,8 +92,9 @@
 static const char *namespace;
 static size_t namespace_len;
 
-static const char *git_dir;
+static const char *git_dir, *git_common_dir;
 static char *git_object_dir, *git_index_file, *git_graft_file;
+int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
 
 /*
  * Repository-local GIT_* environment variables; see cache.h for details.
@@ -111,6 +112,7 @@
 	NO_REPLACE_OBJECTS_ENVIRONMENT,
 	GIT_PREFIX_ENVIRONMENT,
 	GIT_SHALLOW_FILE_ENVIRONMENT,
+	GIT_COMMON_DIR_ENVIRONMENT,
 	NULL
 };
 
@@ -135,14 +137,23 @@
 	return strbuf_detach(&buf, NULL);
 }
 
-static char *git_path_from_env(const char *envvar, const char *path)
+static char *git_path_from_env(const char *envvar, const char *git_dir,
+			       const char *path, int *fromenv)
 {
 	const char *value = getenv(envvar);
-	return value ? xstrdup(value) : git_pathdup("%s", path);
+	if (!value) {
+		char *buf = xmalloc(strlen(git_dir) + strlen(path) + 2);
+		sprintf(buf, "%s/%s", git_dir, path);
+		return buf;
+	}
+	if (fromenv)
+		*fromenv = 1;
+	return xstrdup(value);
 }
 
 static void setup_git_env(void)
 {
+	struct strbuf sb = STRBUF_INIT;
 	const char *gitfile;
 	const char *shallow_file;
 
@@ -151,9 +162,15 @@
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 	gitfile = read_gitfile(git_dir);
 	git_dir = xstrdup(gitfile ? gitfile : git_dir);
-	git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects");
-	git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index");
-	git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts");
+	if (get_common_dir(&sb, git_dir))
+		git_common_dir_env = 1;
+	git_common_dir = strbuf_detach(&sb, NULL);
+	git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir,
+					   "objects", &git_db_env);
+	git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,
+					   "index", &git_index_env);
+	git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,
+					   "info/grafts", &git_graft_env);
 	if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
 		check_replace_refs = 0;
 	namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
@@ -176,6 +193,11 @@
 	return git_dir;
 }
 
+const char *get_git_common_dir(void)
+{
+	return git_common_dir;
+}
+
 const char *get_git_namespace(void)
 {
 	if (!namespace)
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index 710e58c..47ad674 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -20,8 +20,8 @@
 #include "git-compat-util.h"
 #include "ewok.h"
 
-#define MASK(x) ((eword_t)1 << (x % BITS_IN_WORD))
-#define BLOCK(x) (x / BITS_IN_WORD)
+#define EWAH_MASK(x) ((eword_t)1 << (x % BITS_IN_EWORD))
+#define EWAH_BLOCK(x) (x / BITS_IN_EWORD)
 
 struct bitmap *bitmap_new(void)
 {
@@ -33,7 +33,7 @@
 
 void bitmap_set(struct bitmap *self, size_t pos)
 {
-	size_t block = BLOCK(pos);
+	size_t block = EWAH_BLOCK(pos);
 
 	if (block >= self->word_alloc) {
 		size_t old_size = self->word_alloc;
@@ -45,22 +45,22 @@
 			(self->word_alloc - old_size) * sizeof(eword_t));
 	}
 
-	self->words[block] |= MASK(pos);
+	self->words[block] |= EWAH_MASK(pos);
 }
 
 void bitmap_clear(struct bitmap *self, size_t pos)
 {
-	size_t block = BLOCK(pos);
+	size_t block = EWAH_BLOCK(pos);
 
 	if (block < self->word_alloc)
-		self->words[block] &= ~MASK(pos);
+		self->words[block] &= ~EWAH_MASK(pos);
 }
 
 int bitmap_get(struct bitmap *self, size_t pos)
 {
-	size_t block = BLOCK(pos);
+	size_t block = EWAH_BLOCK(pos);
 	return block < self->word_alloc &&
-		(self->words[block] & MASK(pos)) != 0;
+		(self->words[block] & EWAH_MASK(pos)) != 0;
 }
 
 struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)
@@ -127,7 +127,7 @@
 void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
 {
 	size_t original_size = self->word_alloc;
-	size_t other_final = (other->bit_size / BITS_IN_WORD) + 1;
+	size_t other_final = (other->bit_size / BITS_IN_EWORD) + 1;
 	size_t i = 0;
 	struct ewah_iterator it;
 	eword_t word;
@@ -155,17 +155,17 @@
 		uint32_t offset;
 
 		if (word == (eword_t)~0) {
-			for (offset = 0; offset < BITS_IN_WORD; ++offset)
+			for (offset = 0; offset < BITS_IN_EWORD; ++offset)
 				callback(pos++, data);
 		} else {
-			for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+			for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
 				if ((word >> offset) == 0)
 					break;
 
 				offset += ewah_bit_ctz64(word >> offset);
 				callback(pos + offset, data);
 			}
-			pos += BITS_IN_WORD;
+			pos += BITS_IN_EWORD;
 		}
 	}
 }
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
index fccb42b..b522437 100644
--- a/ewah/ewah_bitmap.c
+++ b/ewah/ewah_bitmap.c
@@ -102,7 +102,7 @@
 	if (number == 0)
 		return 0;
 
-	self->bit_size += number * BITS_IN_WORD;
+	self->bit_size += number * BITS_IN_EWORD;
 	return add_empty_words(self, v, number);
 }
 
@@ -152,7 +152,7 @@
 			self->buffer_size += can_add;
 		}
 
-		self->bit_size += can_add * BITS_IN_WORD;
+		self->bit_size += can_add * BITS_IN_EWORD;
 
 		if (number - can_add == 0)
 			break;
@@ -197,7 +197,7 @@
 
 size_t ewah_add(struct ewah_bitmap *self, eword_t word)
 {
-	self->bit_size += BITS_IN_WORD;
+	self->bit_size += BITS_IN_EWORD;
 
 	if (word == 0)
 		return add_empty_word(self, 0);
@@ -211,8 +211,8 @@
 void ewah_set(struct ewah_bitmap *self, size_t i)
 {
 	const size_t dist =
-		(i + BITS_IN_WORD) / BITS_IN_WORD -
-		(self->bit_size + BITS_IN_WORD - 1) / BITS_IN_WORD;
+		(i + BITS_IN_EWORD) / BITS_IN_EWORD -
+		(self->bit_size + BITS_IN_EWORD - 1) / BITS_IN_EWORD;
 
 	assert(i >= self->bit_size);
 
@@ -222,19 +222,19 @@
 		if (dist > 1)
 			add_empty_words(self, 0, dist - 1);
 
-		add_literal(self, (eword_t)1 << (i % BITS_IN_WORD));
+		add_literal(self, (eword_t)1 << (i % BITS_IN_EWORD));
 		return;
 	}
 
 	if (rlw_get_literal_words(self->rlw) == 0) {
 		rlw_set_running_len(self->rlw,
 			rlw_get_running_len(self->rlw) - 1);
-		add_literal(self, (eword_t)1 << (i % BITS_IN_WORD));
+		add_literal(self, (eword_t)1 << (i % BITS_IN_EWORD));
 		return;
 	}
 
 	self->buffer[self->buffer_size - 1] |=
-		((eword_t)1 << (i % BITS_IN_WORD));
+		((eword_t)1 << (i % BITS_IN_EWORD));
 
 	/* check if we just completed a stream of 1s */
 	if (self->buffer[self->buffer_size - 1] == (eword_t)(~0)) {
@@ -255,11 +255,11 @@
 		eword_t *word = &self->buffer[pointer];
 
 		if (rlw_get_run_bit(word)) {
-			size_t len = rlw_get_running_len(word) * BITS_IN_WORD;
+			size_t len = rlw_get_running_len(word) * BITS_IN_EWORD;
 			for (k = 0; k < len; ++k, ++pos)
 				callback(pos, payload);
 		} else {
-			pos += rlw_get_running_len(word) * BITS_IN_WORD;
+			pos += rlw_get_running_len(word) * BITS_IN_EWORD;
 		}
 
 		++pointer;
@@ -268,7 +268,7 @@
 			int c;
 
 			/* todo: zero count optimization */
-			for (c = 0; c < BITS_IN_WORD; ++c, ++pos) {
+			for (c = 0; c < BITS_IN_EWORD; ++c, ++pos) {
 				if ((self->buffer[pointer] & ((eword_t)1 << c)) != 0)
 					callback(pos, payload);
 			}
diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
index 1c2d7af..43481b9 100644
--- a/ewah/ewah_io.c
+++ b/ewah/ewah_io.c
@@ -19,6 +19,7 @@
  */
 #include "git-compat-util.h"
 #include "ewok.h"
+#include "strbuf.h"
 
 int ewah_serialize_native(struct ewah_bitmap *self, int fd)
 {
@@ -110,6 +111,18 @@
 	return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
 }
 
+static int write_strbuf(void *user_data, const void *data, size_t len)
+{
+	struct strbuf *sb = user_data;
+	strbuf_add(sb, data, len);
+	return len;
+}
+
+int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *sb)
+{
+	return ewah_serialize_to(self, write_strbuf, sb);
+}
+
 int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 {
 	const uint8_t *ptr = map;
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 13c6e20..6e2c5e1 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -30,8 +30,9 @@
 #	define ewah_calloc xcalloc
 #endif
 
+struct strbuf;
 typedef uint64_t eword_t;
-#define BITS_IN_WORD (sizeof(eword_t) * 8)
+#define BITS_IN_EWORD (sizeof(eword_t) * 8)
 
 /**
  * Do not use __builtin_popcountll. The GCC implementation
@@ -98,6 +99,7 @@
 		      void *out);
 int ewah_serialize(struct ewah_bitmap *self, int fd);
 int ewah_serialize_native(struct ewah_bitmap *self, int fd);
+int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *);
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd);
 int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
diff --git a/fast-import.c b/fast-import.c
index e78ca10..6378726 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -405,7 +405,7 @@
 
 static void write_crash_report(const char *err)
 {
-	char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+	const char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
 	FILE *rpt = fopen(loc, "w");
 	struct branch *b;
 	unsigned long lu;
@@ -3113,12 +3113,9 @@
 
 static char* make_fast_import_path(const char *path)
 {
-	struct strbuf abs_path = STRBUF_INIT;
-
 	if (!relative_marks_paths || is_absolute_path(path))
 		return xstrdup(path);
-	strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
-	return strbuf_detach(&abs_path, NULL);
+	return xstrdup(git_path("info/fast-import/%s", path));
 }
 
 static void option_import_marks(const char *marks,
diff --git a/fetch-pack.c b/fetch-pack.c
index 48526aa..a136772 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -43,7 +43,12 @@
 #define MAX_IN_VAIN 256
 
 static struct prio_queue rev_list = { compare_commits_by_commit_date };
-static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
+static int non_common_revs, multi_ack, use_sideband;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1	01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1	02
+static unsigned int allow_unadvertised_object_request;
 
 static void rev_list_push(struct commit *commit, int mark)
 {
@@ -60,7 +65,7 @@
 	}
 }
 
-static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
 {
 	struct object *o = deref_tag(parse_object(sha1), refname, 0);
 
@@ -70,9 +75,16 @@
 	return 0;
 }
 
-static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
+				   int flag, void *cb_data)
 {
-	struct object *o = deref_tag(parse_object(sha1), refname, 0);
+	return rev_list_insert_ref(refname, oid->hash);
+}
+
+static int clear_marks(const char *refname, const struct object_id *oid,
+		       int flag, void *cb_data)
+{
+	struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
 
 	if (o && o->type == OBJ_COMMIT)
 		clear_commit_marks((struct commit *)o,
@@ -226,7 +238,7 @@
 
 static void insert_one_alternate_ref(const struct ref *ref, void *unused)
 {
-	rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
+	rev_list_insert_ref(NULL, ref->old_sha1);
 }
 
 #define INITIAL_FLUSH 16
@@ -263,7 +275,7 @@
 		for_each_ref(clear_marks, NULL);
 	marked = 1;
 
-	for_each_ref(rev_list_insert_ref, NULL);
+	for_each_ref(rev_list_insert_ref_oid, NULL);
 	for_each_alternate_ref(insert_one_alternate_ref, NULL);
 
 	fetching = 0;
@@ -466,7 +478,7 @@
 
 static struct commit_list *complete;
 
-static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int mark_complete(const unsigned char *sha1)
 {
 	struct object *o = parse_object(sha1);
 
@@ -487,6 +499,12 @@
 	return 0;
 }
 
+static int mark_complete_oid(const char *refname, const struct object_id *oid,
+			     int flag, void *cb_data)
+{
+	return mark_complete(oid->hash);
+}
+
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
 					 unsigned long cutoff)
 {
@@ -542,7 +560,8 @@
 	}
 
 	/* Append unmatched requests to the list */
-	if (allow_tip_sha1_in_want) {
+	if ((allow_unadvertised_object_request &
+	    (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
 		for (i = 0; i < nr_sought; i++) {
 			unsigned char sha1[20];
 
@@ -564,7 +583,7 @@
 
 static void mark_alternate_complete(const struct ref *ref, void *unused)
 {
-	mark_complete(NULL, ref->old_sha1, 0, NULL);
+	mark_complete(ref->old_sha1);
 }
 
 static int everything_local(struct fetch_pack_args *args,
@@ -599,7 +618,7 @@
 	}
 
 	if (!args->depth) {
-		for_each_ref(mark_complete, NULL);
+		for_each_ref(mark_complete_oid, NULL);
 		for_each_alternate_ref(mark_alternate_complete, NULL);
 		commit_list_sort_by_date(&complete);
 		if (cutoff)
@@ -790,7 +809,7 @@
 	sort_ref_list(&ref, ref_compare_name);
 	qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
 
-	if (is_repository_shallow() && !server_supports("shallow"))
+	if ((args->depth > 0 || is_repository_shallow()) && !server_supports("shallow"))
 		die("Server does not support shallow clients");
 	if (server_supports("multi_ack_detailed")) {
 		if (args->verbose)
@@ -821,7 +840,12 @@
 	if (server_supports("allow-tip-sha1-in-want")) {
 		if (args->verbose)
 			fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
-		allow_tip_sha1_in_want = 1;
+		allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+	}
+	if (server_supports("allow-reachable-sha1-in-want")) {
+		if (args->verbose)
+			fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
+		allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
 	}
 	if (!server_supports("thin-pack"))
 		args->use_thin_pack = 0;
diff --git a/fsck.c b/fsck.c
index 10bcb65..24b2a5f 100644
--- a/fsck.c
+++ b/fsck.c
@@ -241,8 +241,8 @@
 	return retval;
 }
 
-static int require_end_of_header(const void *data, unsigned long size,
-	struct object *obj, fsck_error error_func)
+static int verify_headers(const void *data, unsigned long size,
+			  struct object *obj, fsck_error error_func)
 {
 	const char *buffer = (const char *)data;
 	unsigned long i;
@@ -258,6 +258,15 @@
 		}
 	}
 
+	/*
+	 * We did not find double-LF that separates the header
+	 * and the body.  Not having a body is not a crime but
+	 * we do want to see the terminating LF for the last header
+	 * line.
+	 */
+	if (size && buffer[size - 1] == '\n')
+		return 0;
+
 	return error_func(obj, FSCK_ERROR, "unterminated header");
 }
 
@@ -308,7 +317,7 @@
 	unsigned parent_count, parent_line_count = 0;
 	int err;
 
-	if (require_end_of_header(buffer, size, &commit->object, error_func))
+	if (verify_headers(buffer, size, &commit->object, error_func))
 		return -1;
 
 	if (!skip_prefix(buffer, "tree ", &buffer))
@@ -387,7 +396,7 @@
 		}
 	}
 
-	if (require_end_of_header(buffer, size, &tag->object, error_func))
+	if (verify_headers(buffer, size, &tag->object, error_func))
 		goto done;
 
 	if (!skip_prefix(buffer, "object ", &buffer)) {
diff --git a/generate-cmdlist.perl b/generate-cmdlist.perl
new file mode 100755
index 0000000..31516e3
--- /dev/null
+++ b/generate-cmdlist.perl
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+print <<"EOT";
+/* Automatically generated by $0 */
+
+struct cmdname_help {
+	char name[16];
+	char help[80];
+	unsigned char group;
+};
+
+static char *common_cmd_groups[] = {
+EOT
+
+my $n = 0;
+my %grp;
+while (<>) {
+	last if /^### command list/;
+	next if (1../^### common groups/) || /^#/ || /^\s*$/;
+	chop;
+	my ($k, $v) = split ' ', $_, 2;
+	$grp{$k} = $n++;
+	print "\tN_(\"$v\"),\n";
+}
+
+print "};\n\nstatic struct cmdname_help common_cmds[] = {\n";
+
+while (<>) {
+	next if /^#/ || /^\s*$/;
+	my @tags = split;
+	my $cmd = shift @tags;
+	for my $t (@tags) {
+		if (exists $grp{$t}) {
+			my $s;
+			open my $f, '<', "Documentation/$cmd.txt" or die;
+			while (<$f>) {
+				($s) = /^$cmd - (.+)$/;
+				last if $s;
+			}
+			close $f;
+			$cmd =~ s/^git-//;
+			print "\t{\"$cmd\", N_(\"$s\"), $grp{$t}},\n";
+			last;
+		}
+	}
+}
+
+print "};\n";
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
deleted file mode 100755
index 9a4c9b9..0000000
--- a/generate-cmdlist.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-
-echo "/* Automatically generated by $0 */
-struct cmdname_help {
-    char name[16];
-    char help[80];
-};
-
-static struct cmdname_help common_cmds[] = {"
-
-sed -n -e 's/^git-\([^ 	]*\)[ 	].* common.*/\1/p' command-list.txt |
-sort |
-while read cmd
-do
-     sed -n '
-     /^NAME/,/git-'"$cmd"'/H
-     ${
-	    x
-	    s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", N_("\1")},/
-	    p
-     }' "Documentation/git-$cmd.txt"
-done
-echo "};"
diff --git a/git-am.sh b/git-am.sh
index a67d0f9..8733071 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -69,6 +69,8 @@
 	cmdline="$cmdline -3"
 fi
 
+empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
 sq () {
 	git rev-parse --sq-quote "$@"
 }
@@ -85,7 +87,7 @@
 		return 1
 	fi
 
-	if ! test -s "$dotest/abort-safety"
+	if ! test -f "$dotest/abort-safety"
 	then
 		return 0
 	fi
@@ -177,7 +179,8 @@
     then
 	    GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
     fi
-    git-merge-recursive $orig_tree -- HEAD $his_tree || {
+    our_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree)
+    git-merge-recursive $orig_tree -- $our_tree $his_tree || {
 	    git rerere $allow_rerere_autoupdate
 	    die "$(gettext "Failed to merge in the changes.")"
     }
@@ -378,6 +381,7 @@
 ignore_date=
 allow_rerere_autoupdate=
 gpg_sign_opt=
+threeway=
 
 if test "$(git config --bool --get am.messageid)" = true
 then
@@ -389,6 +393,11 @@
     keepcr=t
 fi
 
+if test "$(git config --bool --get am.threeWay)" = true
+then
+    threeway=t
+fi
+
 while test $# != 0
 do
 	case "$1" in
@@ -400,6 +409,8 @@
 		;;
 	-3|--3way)
 		threeway=t ;;
+	--no-3way)
+		threeway=f ;;
 	-s|--signoff)
 		sign=t ;;
 	-u|--utf8)
@@ -502,10 +513,11 @@
 		;;
 	t,)
 		git rerere clear
-		git read-tree --reset -u HEAD HEAD
-		orig_head=$(cat "$GIT_DIR/ORIG_HEAD")
-		git reset HEAD
-		git update-ref ORIG_HEAD $orig_head
+		head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
+		git read-tree --reset -u $head_tree $head_tree &&
+		index_tree=$(git write-tree) &&
+		git read-tree -m -u $index_tree $head_tree
+		git read-tree $head_tree
 		;;
 	,t)
 		if test -f "$dotest/rebasing"
@@ -515,8 +527,19 @@
 		git rerere clear
 		if safe_to_abort
 		then
-			git read-tree --reset -u HEAD ORIG_HEAD
-			git reset ORIG_HEAD
+			head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
+			git read-tree --reset -u $head_tree $head_tree &&
+			index_tree=$(git write-tree) &&
+			orig_head=$(git rev-parse --verify -q ORIG_HEAD || echo $empty_tree) &&
+			git read-tree -m -u $index_tree $orig_head
+			if git rev-parse --verify -q ORIG_HEAD >/dev/null 2>&1
+			then
+				git reset ORIG_HEAD
+			else
+				git read-tree $empty_tree
+				curr_branch=$(git symbolic-ref HEAD 2>/dev/null) &&
+				git update-ref -d $curr_branch
+			fi
 		fi
 		rm -fr "$dotest"
 		exit ;;
@@ -657,6 +680,8 @@
 if test "$(cat "$dotest/threeway")" = t
 then
 	threeway=t
+else
+	threeway=f
 fi
 git_apply_opt=$(cat "$dotest/apply-opt")
 if test "$(cat "$dotest/sign")" = t
@@ -827,10 +852,10 @@
 		continue
 	fi
 
-	if test -x "$GIT_DIR"/hooks/applypatch-msg
+	hook="$(git rev-parse --git-path hooks/applypatch-msg)"
+	if test -x "$hook"
 	then
-		"$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
-		stop_here $this
+		"$hook" "$dotest/final-commit" || stop_here $this
 	fi
 
 	if test -f "$dotest/final-commit"
@@ -904,9 +929,10 @@
 		stop_here_user_resolve $this
 	fi
 
-	if test -x "$GIT_DIR"/hooks/pre-applypatch
+	hook="$(git rev-parse --git-path hooks/pre-applypatch)"
+	if test -x "$hook"
 	then
-		"$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+		"$hook" || stop_here $this
 	fi
 
 	tree=$(git write-tree) &&
@@ -933,18 +959,17 @@
 		echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
 	fi
 
-	if test -x "$GIT_DIR"/hooks/post-applypatch
-	then
-		"$GIT_DIR"/hooks/post-applypatch
-	fi
+	hook="$(git rev-parse --git-path hooks/post-applypatch)"
+	test -x "$hook" && "$hook"
 
 	go_next
 done
 
 if test -s "$dotest"/rewritten; then
     git notes copy --for-rewrite=rebase < "$dotest"/rewritten
-    if test -x "$GIT_DIR"/hooks/post-rewrite; then
-	"$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+    hook="$(git rev-parse --git-path hooks/post-rewrite)"
+    if test -x "$hook"; then
+	"$hook" rebase < "$dotest"/rewritten
     fi
 fi
 
diff --git a/git-compat-util.h b/git-compat-util.h
index af5af22..c6d391f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -187,6 +187,7 @@
 #elif defined(_MSC_VER)
 #include "compat/msvc.h"
 #else
+#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
@@ -934,4 +935,10 @@
 # define SHELL_PATH "/bin/sh"
 #endif
 
+#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
+#define flockfile(fh)
+#define funlockfile(fh)
+#define getc_unlocked(fh) getc(fh)
+#endif
+
 #endif
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 73d367c..1e4e65a 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -921,7 +921,7 @@
 		# (See check_refname_component in refs.c.)
 		1 while $xtag =~ s/
 			(?: \.\.        # Tag cannot contain '..'.
-			|   \@{         # Tag cannot contain '@{'.
+			|   \@\{        # Tag cannot contain '@{'.
 			| ^ -           # Tag cannot begin with '-'.
 			|   \.lock $    # Tag cannot end with '.lock'.
 			| ^ \.          # Tag cannot begin...
@@ -1162,7 +1162,7 @@
 		die "Fast-forward update failed: $?\n" if $?;
 	}
 	else {
-		system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
+		system(qw(git merge -m cvsimport), "$remote/$opt_o");
 		die "Could not merge $opt_o into the current branch.\n" if $?;
 	}
 } else {
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index fe61e89..54ac8e4 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -2,6 +2,9 @@
 
 : ${MERGE_TOOLS_DIR=$(git --exec-path)/mergetools}
 
+IFS='
+'
+
 mode_ok () {
 	if diff_mode
 	then
@@ -302,6 +305,7 @@
 	EOF
 
 	# Loop over each candidate and stop when a valid merge tool is found.
+	IFS=' '
 	for tool in $tools
 	do
 		is_available "$tool" && echo "$tool" && return 0
diff --git a/git-mergetool.sh b/git-mergetool.sh
index d20581c..9f77e3a 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -451,8 +451,6 @@
 printf "Merging:\n"
 printf "%s\n" "$files"
 
-IFS='
-'
 rc=0
 for i in $files
 do
diff --git a/git-p4.py b/git-p4.py
index 549022e..073f87b 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -43,6 +43,9 @@
 # Only labels/tags matching this will be imported/exported
 defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$'
 
+# Grab changes in blocks of this many revisions, unless otherwise requested
+defaultBlockSize = 512
+
 def p4_build_cmd(cmd):
     """Build a suitable p4 command line.
 
@@ -249,6 +252,10 @@
 def p4_move(src, dest):
     p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
 
+def p4_last_change():
+    results = p4CmdList(["changes", "-m", "1"])
+    return int(results[0]['change'])
+
 def p4_describe(change):
     """Make sure it returns a valid result by checking for
        the presence of field "time".  Return a dict of the
@@ -368,7 +375,7 @@
     # Returns the perforce file type for the given file.
 
     result = p4_read_pipe(["opened", wildcard_encode(file)])
-    match = re.match(".*\((.+)\)\r?$", result)
+    match = re.match(".*\((.+)\)( \*exclusive\*)?\r?$", result)
     if match:
         return match.group(1)
     else:
@@ -502,12 +509,14 @@
 def p4Where(depotPath):
     if not depotPath.endswith("/"):
         depotPath += "/"
-    depotPath = depotPath + "..."
-    outputList = p4CmdList(["where", depotPath])
+    depotPathLong = depotPath + "..."
+    outputList = p4CmdList(["where", depotPathLong])
     output = None
     for entry in outputList:
         if "depotFile" in entry:
-            if entry["depotFile"] == depotPath:
+            # Search for the base client side depot path, as long as it starts with the branch's P4 path.
+            # The base path always ends with "/...".
+            if entry["depotFile"].find(depotPath) == 0 and entry["depotFile"][-4:] == "/...":
                 output = entry
                 break
         elif "data" in entry:
@@ -740,17 +749,77 @@
 def originP4BranchesExist():
         return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
 
-def p4ChangesForPaths(depotPaths, changeRange):
-    assert depotPaths
-    cmd = ['changes']
-    for p in depotPaths:
-        cmd += ["%s...%s" % (p, changeRange)]
-    output = p4_read_pipe_lines(cmd)
 
+def p4ParseNumericChangeRange(parts):
+    changeStart = int(parts[0][1:])
+    if parts[1] == '#head':
+        changeEnd = p4_last_change()
+    else:
+        changeEnd = int(parts[1])
+
+    return (changeStart, changeEnd)
+
+def chooseBlockSize(blockSize):
+    if blockSize:
+        return blockSize
+    else:
+        return defaultBlockSize
+
+def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
+    assert depotPaths
+
+    # Parse the change range into start and end. Try to find integer
+    # revision ranges as these can be broken up into blocks to avoid
+    # hitting server-side limits (maxrows, maxscanresults). But if
+    # that doesn't work, fall back to using the raw revision specifier
+    # strings, without using block mode.
+
+    if changeRange is None or changeRange == '':
+        changeStart = 1
+        changeEnd = p4_last_change()
+        block_size = chooseBlockSize(requestedBlockSize)
+    else:
+        parts = changeRange.split(',')
+        assert len(parts) == 2
+        try:
+            (changeStart, changeEnd) = p4ParseNumericChangeRange(parts)
+            block_size = chooseBlockSize(requestedBlockSize)
+        except:
+            changeStart = parts[0][1:]
+            changeEnd = parts[1]
+            if requestedBlockSize:
+                die("cannot use --changes-block-size with non-numeric revisions")
+            block_size = None
+
+    # Accumulate change numbers in a dictionary to avoid duplicates
     changes = {}
-    for line in output:
-        changeNum = int(line.split(" ")[1])
-        changes[changeNum] = True
+
+    for p in depotPaths:
+        # Retrieve changes a block at a time, to prevent running
+        # into a MaxResults/MaxScanRows error from the server.
+
+        while True:
+            cmd = ['changes']
+
+            if block_size:
+                end = min(changeEnd, changeStart + block_size)
+                revisionRange = "%d,%d" % (changeStart, end)
+            else:
+                revisionRange = "%s,%s" % (changeStart, changeEnd)
+
+            cmd += ["%s...@%s" % (p, revisionRange)]
+
+            for line in p4_read_pipe_lines(cmd):
+                changeNum = int(line.split(" ")[1])
+                changes[changeNum] = True
+
+            if not block_size:
+                break
+
+            if end >= changeEnd:
+                break
+
+            changeStart = end + 1
 
     changelist = changes.keys()
     changelist.sort()
@@ -1220,7 +1289,7 @@
             editor = os.environ.get("P4EDITOR")
         else:
             editor = read_pipe("git var GIT_EDITOR").strip()
-        system([editor, template_file])
+        system(["sh", "-c", ('%s "$@"' % editor), editor, template_file])
 
         # If the file was not saved, prompt to see if this patch should
         # be skipped.  But skip this verification step if configured so.
@@ -1627,7 +1696,10 @@
         if self.useClientSpec:
             self.clientSpecDirs = getClientSpec()
 
-        if self.useClientSpec:
+        # Check for the existance of P4 branches
+        branchesDetected = (len(p4BranchesInGit().keys()) > 1)
+
+        if self.useClientSpec and not branchesDetected:
             # all files are relative to the client spec
             self.clientPath = getClientRoot()
         else:
@@ -1911,7 +1983,10 @@
                 optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
                 optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
                                      help="Import into refs/heads/ , not refs/remotes"),
-                optparse.make_option("--max-changes", dest="maxChanges"),
+                optparse.make_option("--max-changes", dest="maxChanges",
+                                     help="Maximum number of changes to import"),
+                optparse.make_option("--changes-block-size", dest="changes_block_size", type="int",
+                                     help="Internal block size to use when iteratively calling p4 changes"),
                 optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
                                      help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
                 optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
@@ -1940,6 +2015,7 @@
         self.syncWithOrigin = True
         self.importIntoRemotes = True
         self.maxChanges = ""
+        self.changes_block_size = None
         self.keepRepoPath = False
         self.depotPaths = None
         self.p4BranchesInGit = []
@@ -2110,7 +2186,7 @@
             # them back too.  This is not needed to the cygwin windows version,
             # just the native "NT" type.
             #
-            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
+            text = p4_read_pipe(['print', '-q', '-o', '-', "%s@%s" % (file['depotFile'], file['change']) ])
             if p4_version_string().find("/NT") >= 0:
                 text = text.replace("\r\n", "\n")
             contents = [ text ]
@@ -2586,7 +2662,7 @@
         branchPrefix = self.depotPaths[0] + branch + "/"
         range = "@1,%s" % maxChange
         #print "prefix" + branchPrefix
-        changes = p4ChangesForPaths([branchPrefix], range)
+        changes = p4ChangesForPaths([branchPrefix], range, self.changes_block_size)
         if len(changes) <= 0:
             return False
         firstChange = changes[0]
@@ -3002,7 +3078,7 @@
                 if self.verbose:
                     print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
                                                               self.changeRange)
-                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
+                changes = p4ChangesForPaths(self.depotPaths, self.changeRange, self.changes_block_size)
 
                 if len(self.maxChanges) > 0:
                     changes = changes[:min(int(self.maxChanges), len(changes))]
diff --git a/git-pull.sh b/git-pull.sh
index 23781e5..a814bf6 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -4,13 +4,53 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff|--ff-only] [--[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=
+OPTIONS_KEEPDASHDASH=
+OPTIONS_STUCKLONG=Yes
+OPTIONS_SPEC="\
+git pull [options] [<repository> [<refspec>...]]
+
+Fetch one or more remote refs and integrate it/them with the current HEAD.
+--
+v,verbose                  be more verbose
+q,quiet                    be more quiet
+progress                   force progress reporting
+
+  Options related to merging
+r,rebase?false|true|preserve incorporate changes by rebasing rather than merging
+n!                         do not show a diffstat at the end of the merge
+stat                       show a diffstat at the end of the merge
+summary                    (synonym to --stat)
+log?n                      add (at most <n>) entries from shortlog to merge commit message
+squash                     create a single commit instead of doing a merge
+commit                     perform a commit if the merge succeeds (default)
+e,edit                       edit message before committing
+ff                         allow fast-forward
+ff-only!                   abort if fast-forward is not possible
+verify-signatures          verify that the named commit has a valid GPG signature
+s,strategy=strategy        merge strategy to use
+X,strategy-option=option   option for selected merge strategy
+S,gpg-sign?key-id          GPG sign commit
+
+  Options related to fetching
+all                        fetch from all remotes
+a,append                   append to .git/FETCH_HEAD instead of overwriting
+upload-pack=path           path to upload pack on remote end
+f,force                    force overwrite of local branch
+t,tags                     fetch all tags and associated objects
+p,prune                    prune remote-tracking branches no longer on remote
+recurse-submodules?on-demand control recursive fetching of submodules
+dry-run                    dry run
+k,keep                     keep downloaded pack
+depth=depth                deepen history of shallow clone
+unshallow                  convert to a complete repository
+update-shallow             accept refs that update .git/shallow
+refmap=refmap              specify fetch refmap
+"
+test $# -gt 0 && args="$*"
 . git-sh-setup
 . git-sh-i18n
-set_reflog_action "pull${1+ $*}"
+set_reflog_action "pull${args+ $args}"
 require_work_tree_exists
 cd_to_toplevel
 
@@ -44,7 +84,8 @@
 
 strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
 log_arg= verbosity= progress= recurse_submodules= verify_signatures=
-merge_args= edit= rebase_args=
+merge_args= edit= rebase_args= all= append= upload_pack= force= tags= prune=
+keep= depth= unshallow= update_shallow= refmap=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short="${curr_branch#refs/heads/}"
 rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
@@ -86,17 +127,17 @@
 		diffstat=--stat ;;
 	--log|--log=*|--no-log)
 		log_arg="$1" ;;
-	--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
+	--no-commit)
 		no_commit=--no-commit ;;
-	--c|--co|--com|--comm|--commi|--commit)
+	--commit)
 		no_commit=--commit ;;
 	-e|--edit)
 		edit=--edit ;;
 	--no-edit)
 		edit=--no-edit ;;
-	--sq|--squ|--squa|--squas|--squash)
+	--squash)
 		squash=--squash ;;
-	--no-sq|--no-squ|--no-squa|--no-squas|--no-squash)
+	--no-squash)
 		squash=--no-squash ;;
 	--ff)
 		no_ff=--ff ;;
@@ -104,39 +145,19 @@
 		no_ff=--no-ff ;;
 	--ff-only)
 		ff_only=--ff-only ;;
-	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
-		--strateg=*|--strategy=*|\
-	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
-		case "$#,$1" in
-		*,*=*)
-			strategy=$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
-		1,*)
-			usage ;;
-		*)
-			strategy="$2"
-			shift ;;
-		esac
-		strategy_args="${strategy_args}-s $strategy "
+	-s*|--strategy=*)
+		strategy_args="$strategy_args $1"
 		;;
-	-X*)
-		case "$#,$1" in
-		1,-X)
-			usage ;;
-		*,-X)
-			xx="-X $(git rev-parse --sq-quote "$2")"
-			shift ;;
-		*,*)
-			xx=$(git rev-parse --sq-quote "$1") ;;
-		esac
-		merge_args="$merge_args$xx "
+	-X*|--strategy-option=*)
+		merge_args="$merge_args $(git rev-parse --sq-quote "$1")"
 		;;
-	-r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)
+	-r*|--rebase=*)
 		rebase="${1#*=}"
 		;;
-	-r|--r|--re|--reb|--reba|--rebas|--rebase)
+	--rebase)
 		rebase=true
 		;;
-	--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
+	--no-rebase)
 		rebase=false
 		;;
 	--recurse-submodules)
@@ -163,16 +184,41 @@
 	-S*)
 		gpg_sign_args=$(git rev-parse --sq-quote "$1")
 		;;
-	--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
+	--dry-run)
 		dry_run=--dry-run
 		;;
+	--all|--no-all)
+		all=$1 ;;
+	-a|--append|--no-append)
+		append=$1 ;;
+	--upload-pack=*|--no-upload-pack)
+		upload_pack=$1 ;;
+	-f|--force|--no-force)
+		force="$force $1" ;;
+	-t|--tags|--no-tags)
+		tags=$1 ;;
+	-p|--prune|--no-prune)
+		prune=$1 ;;
+	-k|--keep|--no-keep)
+		keep=$1 ;;
+	--depth=*|--no-depth)
+		depth=$1 ;;
+	--unshallow|--no-unshallow)
+		unshallow=$1 ;;
+	--update-shallow|--no-update-shallow)
+		update_shallow=$1 ;;
+	--refmap=*|--no-refmap)
+		refmap=$1 ;;
 	-h|--help-all)
 		usage
 		;;
-	*)
-		# Pass thru anything that may be meant for fetch.
+	--)
+		shift
 		break
 		;;
+	*)
+		usage
+		;;
 	esac
 	shift
 done
@@ -234,7 +280,7 @@
 	if ! git rev-parse -q --verify HEAD >/dev/null
 	then
 		# On an unborn branch
-		if test -f "$GIT_DIR/index"
+		if test -f "$(git rev-parse --git-path index)"
 		then
 			die "$(gettext "updating an unborn branch with changes added to the index")"
 		fi
@@ -248,7 +294,9 @@
 	oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch 2>/dev/null)
 }
 orig_head=$(git rev-parse -q --verify HEAD)
-git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
+git fetch $verbosity $progress $dry_run $recurse_submodules $all $append \
+$upload_pack $force $tags $prune $keep $depth $unshallow $update_shallow \
+$refmap --update-head-ok "$@" || exit 1
 test -z "$dry_run" || exit 0
 
 curr_head=$(git rev-parse -q --verify HEAD)
@@ -317,7 +365,6 @@
 	fi
 fi
 
-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 $rebase_args $verbosity"
@@ -328,7 +375,7 @@
 	eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
 	eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
 	eval="$eval $gpg_sign_args"
-	eval="$eval \"\$merge_name\" HEAD $merge_head"
+	eval="$eval FETCH_HEAD"
 	;;
 esac
 eval "exec $eval"
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index f923732..9ae898b 100644
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -78,7 +78,7 @@
 
 		As a result, git cannot rebase them.
 		EOF
-		return $?
+		return $ret
 	fi
 
 	git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8c5de4c..5ff0f1c 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -655,9 +655,9 @@
 		git notes copy --for-rewrite=rebase < "$rewritten_list" ||
 		true # we don't care if this copying failed
 	} &&
-	if test -x "$GIT_DIR"/hooks/post-rewrite &&
-		test -s "$rewritten_list"; then
-		"$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
+	hook="$(git rev-parse --git-path hooks/post-rewrite)"
+	if test -x "$hook" && test -s "$rewritten_list"; then
+		"$hook" rebase < "$rewritten_list"
 		true # we don't care if this hook failed
 	fi &&
 	warn "Successfully rebased and updated $head_name."
@@ -849,7 +849,11 @@
 	# do we have anything to commit?
 	if git diff-index --cached --quiet HEAD --
 	then
-		: Nothing to commit -- skip this
+		# Nothing to commit -- skip this commit
+
+		test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
+		rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
+		die "Could not remove CHERRY_PICK_HEAD"
 	else
 		if ! test -f "$author_script"
 		then
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index d3fb67d..2cc2a6d 100644
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -94,10 +94,8 @@
 	if test -s "$state_dir"/rewritten
 	then
 		git notes copy --for-rewrite=rebase <"$state_dir"/rewritten
-		if test -x "$GIT_DIR"/hooks/post-rewrite
-		then
-			"$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
-		fi
+		hook="$(git rev-parse --git-path hooks/post-rewrite)"
+		test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten
 	fi
 	say All done.
 }
diff --git a/git-rebase.sh b/git-rebase.sh
index 90854e3..1757404 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -202,9 +202,9 @@
 
 run_pre_rebase_hook () {
 	if test -z "$ok_to_skip_pre_rebase" &&
-	   test -x "$GIT_DIR/hooks/pre-rebase"
+	   test -x "$(git rev-parse --git-path hooks/pre-rebase)"
 	then
-		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+		"$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
 		die "$(gettext "The pre-rebase hook refused to rebase.")"
 	fi
 }
diff --git a/git-send-email.perl b/git-send-email.perl
index e1e9b14..ae9f869 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -487,6 +487,37 @@
 }
 
 my %aliases;
+
+sub parse_sendmail_alias {
+	local $_ = shift;
+	if (/"/) {
+		print STDERR "warning: sendmail alias with quotes is not supported: $_\n";
+	} elsif (/:include:/) {
+		print STDERR "warning: `:include:` not supported: $_\n";
+	} elsif (/[\/|]/) {
+		print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n";
+	} elsif (/^(\S+?)\s*:\s*(.+)$/) {
+		my ($alias, $addr) = ($1, $2);
+		$aliases{$alias} = [ split_addrs($addr) ];
+	} else {
+		print STDERR "warning: sendmail line is not recognized: $_\n";
+	}
+}
+
+sub parse_sendmail_aliases {
+	my $fh = shift;
+	my $s = '';
+	while (<$fh>) {
+		chomp;
+		next if /^\s*$/ || /^\s*#/;
+		$s .= $_, next if $s =~ s/\\$// || s/^\s+//;
+		parse_sendmail_alias($s) if $s;
+		$s = $_;
+	}
+	$s =~ s/\\$//; # silently tolerate stray '\' on last line
+	parse_sendmail_alias($s) if $s;
+}
+
 my %parse_alias = (
 	# multiline formats can be supported in the future
 	mutt => sub { my $fh = shift; while (<$fh>) {
@@ -515,7 +546,7 @@
 			       $aliases{$alias} = [ split_addrs($addr) ];
 			  }
 		      } },
-
+	sendmail => \&parse_sendmail_aliases,
 	gnus => sub { my $fh = shift; while (<$fh>) {
 		if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
 			$aliases{$1} = [ $2 ];
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index c42c6e6..4691fbc 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -344,7 +344,7 @@
 		echo >&2 "Unable to determine absolute path of git directory"
 		exit 1
 	}
-	: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+	: ${GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)"}
 }
 
 if test -z "$NONGIT_OK"
diff --git a/git-stash.sh b/git-stash.sh
index 23bd1d3..8e9e2cd 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -20,7 +20,7 @@
 cd_to_toplevel
 
 TMP="$GIT_DIR/.git-stash.$$"
-TMPindex=${GIT_INDEX_FILE-"$GIT_DIR/index"}.stash.$$
+TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
 trap 'rm -f "$TMP-"* "$TMPindex"' 0
 
 ref_stash=refs/stash
@@ -184,7 +184,7 @@
 	fi
 
 	# Make sure the reflog for stash is kept.
-	: >>"$GIT_DIR/logs/$ref_stash"
+	: >>"$(git rev-parse --git-path logs/$ref_stash)"
 	git update-ref -m "$stash_msg" $ref_stash $w_commit
 	ret=$?
 	test $ret != 0 && test -z $quiet &&
@@ -262,7 +262,7 @@
 		say "$(gettext "No local changes to save")"
 		exit 0
 	fi
-	test -f "$GIT_DIR/logs/$ref_stash" ||
+	test -f "$(git rev-parse --git-path logs/$ref_stash)" ||
 		clear_stash || die "$(gettext "Cannot initialize stash")"
 
 	create_stash "$stash_msg" $untracked
diff --git a/git.c b/git.c
index 42a4ee5..44374b1 100644
--- a/git.c
+++ b/git.c
@@ -382,7 +382,7 @@
 	{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
 	{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
 	{ "check-ref-format", cmd_check_ref_format },
-	{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
+	{ "checkout", cmd_checkout, RUN_SETUP },
 	{ "checkout-index", cmd_checkout_index,
 		RUN_SETUP | NEED_WORK_TREE},
 	{ "cherry", cmd_cherry, RUN_SETUP },
diff --git a/help.c b/help.c
index 2072a87..d996b34 100644
--- a/help.c
+++ b/help.c
@@ -218,17 +218,39 @@
 	}
 }
 
+static int cmd_group_cmp(const void *elem1, const void *elem2)
+{
+	const struct cmdname_help *e1 = elem1;
+	const struct cmdname_help *e2 = elem2;
+
+	if (e1->group < e2->group)
+		return -1;
+	if (e1->group > e2->group)
+		return 1;
+	return strcmp(e1->name, e2->name);
+}
+
 void list_common_cmds_help(void)
 {
 	int i, longest = 0;
+	int current_grp = -1;
 
 	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
 		if (longest < strlen(common_cmds[i].name))
 			longest = strlen(common_cmds[i].name);
 	}
 
-	puts(_("The most commonly used git commands are:"));
+	qsort(common_cmds, ARRAY_SIZE(common_cmds),
+		sizeof(common_cmds[0]), cmd_group_cmp);
+
+	puts(_("These are common Git commands used in various situations:"));
+
 	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		if (common_cmds[i].group != current_grp) {
+			printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group]));
+			current_grp = common_cmds[i].group;
+		}
+
 		printf("   %s   ", common_cmds[i].name);
 		mput_char(' ', longest - strlen(common_cmds[i].name));
 		puts(_(common_cmds[i].help));
@@ -372,7 +394,7 @@
 		if (autocorrect > 0) {
 			fprintf_ln(stderr, _("in %0.1f seconds automatically..."),
 				(float)autocorrect/10.0);
-			poll(NULL, 0, autocorrect * 100);
+			sleep_millisec(autocorrect * 100);
 		}
 		return assumed;
 	}
@@ -407,7 +429,7 @@
 	struct string_list *similar_refs;
 };
 
-static int append_similar_ref(const char *refname, const unsigned char *sha1,
+static int append_similar_ref(const char *refname, const struct object_id *oid,
 			      int flags, void *cb_data)
 {
 	struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
diff --git a/hex.c b/hex.c
index cfd9d72..899b74a 100644
--- a/hex.c
+++ b/hex.c
@@ -38,7 +38,7 @@
 int get_sha1_hex(const char *hex, unsigned char *sha1)
 {
 	int i;
-	for (i = 0; i < 20; i++) {
+	for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
 		unsigned int val;
 		/*
 		 * hex[1]=='\0' is caught when val is checked below,
@@ -56,15 +56,20 @@
 	return 0;
 }
 
+int get_oid_hex(const char *hex, struct object_id *oid)
+{
+	return get_sha1_hex(hex, oid->hash);
+}
+
 char *sha1_to_hex(const unsigned char *sha1)
 {
 	static int bufno;
-	static char hexbuffer[4][41];
+	static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
 	static const char hex[] = "0123456789abcdef";
 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
 	int i;
 
-	for (i = 0; i < 20; i++) {
+	for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
 		unsigned int val = *sha1++;
 		*buf++ = hex[val >> 4];
 		*buf++ = hex[val & 0xf];
@@ -73,3 +78,8 @@
 
 	return buffer;
 }
+
+char *oid_to_hex(const struct object_id *oid)
+{
+	return sha1_to_hex(oid->hash);
+}
diff --git a/http-backend.c b/http-backend.c
index 6bf139b..501bf79 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -421,16 +421,16 @@
 		exit(1);
 }
 
-static int show_text_ref(const char *name, const unsigned char *sha1,
-	int flag, void *cb_data)
+static int show_text_ref(const char *name, const struct object_id *oid,
+			 int flag, void *cb_data)
 {
 	const char *name_nons = strip_namespace(name);
 	struct strbuf *buf = cb_data;
-	struct object *o = parse_object(sha1);
+	struct object *o = parse_object(oid->hash);
 	if (!o)
 		return 0;
 
-	strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons);
+	strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
 	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, name, 0);
 		if (!o)
@@ -473,21 +473,21 @@
 	strbuf_release(&buf);
 }
 
-static int show_head_ref(const char *refname, const unsigned char *sha1,
-	int flag, void *cb_data)
+static int show_head_ref(const char *refname, const struct object_id *oid,
+			 int flag, void *cb_data)
 {
 	struct strbuf *buf = cb_data;
 
 	if (flag & REF_ISSYMREF) {
-		unsigned char unused[20];
+		struct object_id unused;
 		const char *target = resolve_ref_unsafe(refname,
 							RESOLVE_REF_READING,
-							unused, NULL);
+							unused.hash, NULL);
 		const char *target_nons = strip_namespace(target);
 
 		strbuf_addf(buf, "ref: %s\n", target_nons);
 	} else {
-		strbuf_addf(buf, "%s\n", sha1_to_hex(sha1));
+		strbuf_addf(buf, "%s\n", oid_to_hex(oid));
 	}
 
 	return 0;
diff --git a/http.c b/http.c
index 4b179f6..e9c6fdd 100644
--- a/http.c
+++ b/http.c
@@ -36,6 +36,7 @@
 static int curl_ssl_verify = -1;
 static int curl_ssl_try;
 static const char *ssl_cert;
+static const char *ssl_cipherlist;
 #if LIBCURL_VERSION_NUM >= 0x070903
 static const char *ssl_key;
 #endif
@@ -187,6 +188,8 @@
 		curl_ssl_verify = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp("http.sslcipherlist", var))
+		return git_config_string(&ssl_cipherlist, var, value);
 	if (!strcmp("http.sslcert", var))
 		return git_config_string(&ssl_cert, var, value);
 #if LIBCURL_VERSION_NUM >= 0x070903
@@ -361,6 +364,13 @@
 	if (http_proactive_auth)
 		init_curl_http_auth(result);
 
+	if (getenv("GIT_SSL_CIPHER_LIST"))
+		ssl_cipherlist = getenv("GIT_SSL_CIPHER_LIST");
+
+	if (ssl_cipherlist != NULL && *ssl_cipherlist)
+		curl_easy_setopt(result, CURLOPT_SSL_CIPHER_LIST,
+				ssl_cipherlist);
+
 	if (ssl_cert != NULL)
 		curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
 	if (has_cert_password())
@@ -406,10 +416,10 @@
 
 	if (curl_http_proxy) {
 		curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
-#if LIBCURL_VERSION_NUM >= 0x070a07
-		curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
-#endif
 	}
+#if LIBCURL_VERSION_NUM >= 0x070a07
+	curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
+#endif
 
 	set_curl_keepalive(result);
 
diff --git a/ll-merge.c b/ll-merge.c
index 8ea03e5..fc3c049 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -9,6 +9,7 @@
 #include "xdiff-interface.h"
 #include "run-command.h"
 #include "ll-merge.h"
+#include "quote.h"
 
 struct ll_merge_driver;
 
@@ -166,17 +167,20 @@
 {
 	char temp[4][50];
 	struct strbuf cmd = STRBUF_INIT;
-	struct strbuf_expand_dict_entry dict[5];
+	struct strbuf_expand_dict_entry dict[6];
+	struct strbuf path_sq = STRBUF_INIT;
 	const char *args[] = { NULL, NULL };
 	int status, fd, i;
 	struct stat st;
 	assert(opts);
 
+	sq_quote_buf(&path_sq, path);
 	dict[0].placeholder = "O"; dict[0].value = temp[0];
 	dict[1].placeholder = "A"; dict[1].value = temp[1];
 	dict[2].placeholder = "B"; dict[2].value = temp[2];
 	dict[3].placeholder = "L"; dict[3].value = temp[3];
-	dict[4].placeholder = NULL; dict[4].value = NULL;
+	dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
+	dict[5].placeholder = NULL; dict[5].value = NULL;
 
 	if (fn->cmdline == NULL)
 		die("custom merge driver %s lacks command line.", fn->name);
@@ -210,6 +214,7 @@
 	for (i = 0; i < 3; i++)
 		unlink_or_warn(temp[i]);
 	strbuf_release(&cmd);
+	strbuf_release(&path_sq);
 	return status;
 }
 
@@ -269,6 +274,7 @@
 		 *    %A - temporary file name for our version.
 		 *    %B - temporary file name for the other branches' version.
 		 *    %L - conflict marker length
+		 *    %P - the original path (safely quoted for the shell)
 		 *
 		 * The external merge driver should write the results in the
 		 * file named by %A, and signal that it has done with zero exit
diff --git a/lockfile.c b/lockfile.c
index 9889277..993bb82 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -157,6 +157,67 @@
 	return lk->fd;
 }
 
+/*
+ * Constants defining the gaps between attempts to lock a file. The
+ * first backoff period is approximately INITIAL_BACKOFF_MS
+ * milliseconds. The longest backoff period is approximately
+ * (BACKOFF_MAX_MULTIPLIER * INITIAL_BACKOFF_MS) milliseconds.
+ */
+#define INITIAL_BACKOFF_MS 1L
+#define BACKOFF_MAX_MULTIPLIER 1000
+
+/*
+ * Try locking path, retrying with quadratic backoff for at least
+ * timeout_ms milliseconds. If timeout_ms is 0, try locking the file
+ * exactly once. If timeout_ms is -1, try indefinitely.
+ */
+static int lock_file_timeout(struct lock_file *lk, const char *path,
+			     int flags, long timeout_ms)
+{
+	int n = 1;
+	int multiplier = 1;
+	long remaining_ms = 0;
+	static int random_initialized = 0;
+
+	if (timeout_ms == 0)
+		return lock_file(lk, path, flags);
+
+	if (!random_initialized) {
+		srand((unsigned int)getpid());
+		random_initialized = 1;
+	}
+
+	if (timeout_ms > 0)
+		remaining_ms = timeout_ms;
+
+	while (1) {
+		long backoff_ms, wait_ms;
+		int fd;
+
+		fd = lock_file(lk, path, flags);
+
+		if (fd >= 0)
+			return fd; /* success */
+		else if (errno != EEXIST)
+			return -1; /* failure other than lock held */
+		else if (timeout_ms > 0 && remaining_ms <= 0)
+			return -1; /* failure due to timeout */
+
+		backoff_ms = multiplier * INITIAL_BACKOFF_MS;
+		/* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
+		wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
+		sleep_millisec(wait_ms);
+		remaining_ms -= wait_ms;
+
+		/* Recursion: (n+1)^2 = n^2 + 2n + 1 */
+		multiplier += 2*n + 1;
+		if (multiplier > BACKOFF_MAX_MULTIPLIER)
+			multiplier = BACKOFF_MAX_MULTIPLIER;
+		else
+			n++;
+	}
+}
+
 void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
 {
 	if (err == EEXIST) {
@@ -179,9 +240,10 @@
 }
 
 /* This should return a meaningful errno on failure */
-int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
+int hold_lock_file_for_update_timeout(struct lock_file *lk, const char *path,
+				      int flags, long timeout_ms)
 {
-	int fd = lock_file(lk, path, flags);
+	int fd = lock_file_timeout(lk, path, flags, timeout_ms);
 	if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
 		unable_to_lock_die(path, errno);
 	return fd;
@@ -214,7 +276,7 @@
 		int save_errno = errno;
 
 		if (flags & LOCK_DIE_ON_ERROR)
-			exit(128);
+			die("failed to prepare '%s' for appending", path);
 		close(orig_fd);
 		rollback_lock_file(lk);
 		errno = save_errno;
diff --git a/lockfile.h b/lockfile.h
index cd2ec95..b4abc61 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -74,8 +74,20 @@
 extern void unable_to_lock_message(const char *path, int err,
 				   struct strbuf *buf);
 extern NORETURN void unable_to_lock_die(const char *path, int err);
-extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
-extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
+extern int hold_lock_file_for_update_timeout(
+		struct lock_file *lk, const char *path,
+		int flags, long timeout_ms);
+
+static inline int hold_lock_file_for_update(
+		struct lock_file *lk, const char *path,
+		int flags)
+{
+	return hold_lock_file_for_update_timeout(lk, path, flags, 0);
+}
+
+extern int hold_lock_file_for_append(struct lock_file *lk, const char *path,
+				     int flags);
+
 extern FILE *fdopen_lock_file(struct lock_file *, const char *mode);
 extern char *get_locked_file_path(struct lock_file *);
 extern int commit_lock_file_to(struct lock_file *, const char *path);
diff --git a/log-tree.c b/log-tree.c
index c931615..01beb11 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -89,7 +89,8 @@
 	return lookup_decoration(&name_decoration, obj);
 }
 
-static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int add_ref_decoration(const char *refname, const struct object_id *oid,
+			      int flags, void *cb_data)
 {
 	struct object *obj;
 	enum decoration_type type = DECORATION_NONE;
@@ -97,20 +98,20 @@
 	assert(cb_data == NULL);
 
 	if (starts_with(refname, "refs/replace/")) {
-		unsigned char original_sha1[20];
+		struct object_id original_oid;
 		if (!check_replace_refs)
 			return 0;
-		if (get_sha1_hex(refname + 13, original_sha1)) {
+		if (get_oid_hex(refname + 13, &original_oid)) {
 			warning("invalid replace ref %s", refname);
 			return 0;
 		}
-		obj = parse_object(original_sha1);
+		obj = parse_object(original_oid.hash);
 		if (obj)
 			add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
 		return 0;
 	}
 
-	obj = parse_object(sha1);
+	obj = parse_object(oid->hash);
 	if (!obj)
 		return 0;
 
@@ -139,7 +140,7 @@
 
 static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
 {
-	struct commit *commit = lookup_commit(graft->sha1);
+	struct commit *commit = lookup_commit(graft->oid.hash);
 	if (!commit)
 		return 0;
 	add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
@@ -149,6 +150,7 @@
 void load_ref_decorations(int flags)
 {
 	if (!decoration_loaded) {
+
 		decoration_loaded = 1;
 		decoration_flags = flags;
 		for_each_ref(add_ref_decoration, NULL);
diff --git a/mergetools/winmerge b/mergetools/winmerge
new file mode 100644
index 0000000..74a66d4
--- /dev/null
+++ b/mergetools/winmerge
@@ -0,0 +1,36 @@
+diff_cmd () {
+	"$merge_tool_path" -u -e "$LOCAL" "$REMOTE"
+	return 0
+}
+
+merge_cmd () {
+	# mergetool.winmerge.trustExitCode is implicitly false.
+	# touch $BACKUP so that we can check_unchanged.
+	touch "$BACKUP"
+	"$merge_tool_path" -u -e -dl Local -dr Remote \
+		"$LOCAL" "$REMOTE" "$MERGED"
+	check_unchanged
+}
+
+translate_merge_tool_path() {
+	# Use WinMergeU.exe if it exists in $PATH
+	if type -p WinMergeU.exe >/dev/null 2>&1
+	then
+		printf WinMergeU.exe
+		return
+	fi
+
+	# Look for WinMergeU.exe in the typical locations
+	winmerge_exe="WinMerge/WinMergeU.exe"
+	for directory in $(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' |
+		cut -d '=' -f 2- | sort -u)
+	do
+		if test -n "$directory" && test -x "$directory/$winmerge_exe"
+		then
+			printf '%s' "$directory/$winmerge_exe"
+			return
+		fi
+	done
+
+	printf WinMergeU.exe
+}
diff --git a/notes-merge.c b/notes-merge.c
index 109ff4e..0b2b82c 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -280,7 +280,7 @@
 				    "(%s exists).", git_path("NOTES_MERGE_*"));
 		}
 
-		if (safe_create_leading_directories(git_path(
+		if (safe_create_leading_directories_const(git_path(
 				NOTES_MERGE_WORKTREE "/.test")))
 			die_errno("unable to create directory %s",
 				  git_path(NOTES_MERGE_WORKTREE));
@@ -295,8 +295,8 @@
 				  const char *buf, unsigned long size)
 {
 	int fd;
-	char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
-	if (safe_create_leading_directories(path))
+	const char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+	if (safe_create_leading_directories_const(path))
 		die_errno("unable to create directory for '%s'", path);
 	if (file_exists(path))
 		die("found existing file at '%s'", path);
diff --git a/notes.c b/notes.c
index 2be4d7f..df08209 100644
--- a/notes.c
+++ b/notes.c
@@ -918,7 +918,7 @@
 	return ret;
 }
 
-static int string_list_add_one_ref(const char *refname, const unsigned char *sha1,
+static int string_list_add_one_ref(const char *refname, const struct object_id *oid,
 				   int flag, void *cb)
 {
 	struct string_list *refs = cb;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 2b3ff23..637770a 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -622,7 +622,7 @@
 	while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
 		eword_t word = objects->words[i] & filter;
 
-		for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+		for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
 			const unsigned char *sha1;
 			struct revindex_entry *entry;
 			uint32_t hash = 0;
@@ -644,7 +644,7 @@
 			show_reach(sha1, object_type, 0, hash, bitmap_git.pack, entry->offset);
 		}
 
-		pos += BITS_IN_WORD;
+		pos += BITS_IN_EWORD;
 		i++;
 	}
 }
@@ -776,7 +776,7 @@
 			break;
 		}
 
-		reuse_objects += BITS_IN_WORD;
+		reuse_objects += BITS_IN_EWORD;
 	}
 
 #ifdef GIT_BITMAP_DEBUG
@@ -1001,7 +1001,7 @@
 	while (ewah_iterator_next(&word, &it)) {
 		uint32_t offset, bit_pos;
 
-		for (offset = 0; offset < BITS_IN_WORD; ++offset) {
+		for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
 			if ((word >> offset) == 0)
 				break;
 
@@ -1014,7 +1014,7 @@
 				return -1;
 		}
 
-		pos += BITS_IN_WORD;
+		pos += BITS_IN_EWORD;
 	}
 	return 0;
 }
diff --git a/pager.c b/pager.c
index 98b2682..070dc11 100644
--- a/pager.c
+++ b/pager.c
@@ -78,6 +78,7 @@
 		argv_array_push(&pager_process.env_array, "LESS=FRX");
 	if (!getenv("LV"))
 		argv_array_push(&pager_process.env_array, "LV=-c");
+	argv_array_push(&pager_process.env_array, "GIT_PAGER_IN_USE");
 	if (start_command(&pager_process))
 		return;
 
diff --git a/path.c b/path.c
index 6b537cc..10f4cbf 100644
--- a/path.c
+++ b/path.c
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "strbuf.h"
 #include "string-list.h"
+#include "dir.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -16,11 +17,15 @@
 
 static char bad_path[] = "/bad-path/";
 
-static char *get_pathname(void)
+static struct strbuf *get_pathname(void)
 {
-	static char pathname_array[4][PATH_MAX];
+	static struct strbuf pathname_array[4] = {
+		STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
+	};
 	static int index;
-	return pathname_array[3 & ++index];
+	struct strbuf *sb = &pathname_array[3 & ++index];
+	strbuf_reset(sb);
+	return sb;
 }
 
 static char *cleanup_path(char *path)
@@ -34,6 +39,13 @@
 	return path;
 }
 
+static void strbuf_cleanup_path(struct strbuf *sb)
+{
+	char *path = cleanup_path(sb->buf);
+	if (path > sb->buf)
+		strbuf_remove(sb, 0, path - sb->buf);
+}
+
 char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 {
 	va_list args;
@@ -49,124 +61,192 @@
 	return cleanup_path(buf);
 }
 
-static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+static int dir_prefix(const char *buf, const char *dir)
 {
-	const char *git_dir = get_git_dir();
-	size_t len;
-
-	len = strlen(git_dir);
-	if (n < len + 1)
-		goto bad;
-	memcpy(buf, git_dir, len);
-	if (len && !is_dir_sep(git_dir[len-1]))
-		buf[len++] = '/';
-	len += vsnprintf(buf + len, n - len, fmt, args);
-	if (len >= n)
-		goto bad;
-	return cleanup_path(buf);
-bad:
-	strlcpy(buf, bad_path, n);
-	return buf;
+	int len = strlen(dir);
+	return !strncmp(buf, dir, len) &&
+		(is_dir_sep(buf[len]) || buf[len] == '\0');
 }
 
-char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+/* $buf =~ m|$dir/+$file| but without regex */
+static int is_dir_file(const char *buf, const char *dir, const char *file)
 {
-	char *ret;
+	int len = strlen(dir);
+	if (strncmp(buf, dir, len) || !is_dir_sep(buf[len]))
+		return 0;
+	while (is_dir_sep(buf[len]))
+		len++;
+	return !strcmp(buf + len, file);
+}
+
+static void replace_dir(struct strbuf *buf, int len, const char *newdir)
+{
+	int newlen = strlen(newdir);
+	int need_sep = (buf->buf[len] && !is_dir_sep(buf->buf[len])) &&
+		!is_dir_sep(newdir[newlen - 1]);
+	if (need_sep)
+		len--;	 /* keep one char, to be replaced with '/'  */
+	strbuf_splice(buf, 0, len, newdir, newlen);
+	if (need_sep)
+		buf->buf[newlen] = '/';
+}
+
+static const char *common_list[] = {
+	"/branches", "/hooks", "/info", "!/logs", "/lost-found",
+	"/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn",
+	"config", "!gc.pid", "packed-refs", "shallow",
+	NULL
+};
+
+static void update_common_dir(struct strbuf *buf, int git_dir_len)
+{
+	char *base = buf->buf + git_dir_len;
+	const char **p;
+
+	if (is_dir_file(base, "logs", "HEAD") ||
+	    is_dir_file(base, "info", "sparse-checkout"))
+		return;	/* keep this in $GIT_DIR */
+	for (p = common_list; *p; p++) {
+		const char *path = *p;
+		int is_dir = 0;
+		if (*path == '!')
+			path++;
+		if (*path == '/') {
+			path++;
+			is_dir = 1;
+		}
+		if (is_dir && dir_prefix(base, path)) {
+			replace_dir(buf, git_dir_len, get_git_common_dir());
+			return;
+		}
+		if (!is_dir && !strcmp(base, path)) {
+			replace_dir(buf, git_dir_len, get_git_common_dir());
+			return;
+		}
+	}
+}
+
+void report_linked_checkout_garbage(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char **p;
+	int len;
+
+	if (!git_common_dir_env)
+		return;
+	strbuf_addf(&sb, "%s/", get_git_dir());
+	len = sb.len;
+	for (p = common_list; *p; p++) {
+		const char *path = *p;
+		if (*path == '!')
+			continue;
+		strbuf_setlen(&sb, len);
+		strbuf_addstr(&sb, path);
+		if (file_exists(sb.buf))
+			report_garbage("unused in linked checkout", sb.buf);
+	}
+	strbuf_release(&sb);
+}
+
+static void adjust_git_path(struct strbuf *buf, int git_dir_len)
+{
+	const char *base = buf->buf + git_dir_len;
+	if (git_graft_env && is_dir_file(base, "info", "grafts"))
+		strbuf_splice(buf, 0, buf->len,
+			      get_graft_file(), strlen(get_graft_file()));
+	else if (git_index_env && !strcmp(base, "index"))
+		strbuf_splice(buf, 0, buf->len,
+			      get_index_file(), strlen(get_index_file()));
+	else if (git_db_env && dir_prefix(base, "objects"))
+		replace_dir(buf, git_dir_len + 7, get_object_directory());
+	else if (git_common_dir_env)
+		update_common_dir(buf, git_dir_len);
+}
+
+static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+{
+	int gitdir_len;
+	strbuf_addstr(buf, get_git_dir());
+	if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+		strbuf_addch(buf, '/');
+	gitdir_len = buf->len;
+	strbuf_vaddf(buf, fmt, args);
+	adjust_git_path(buf, gitdir_len);
+	strbuf_cleanup_path(buf);
+}
+
+void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+{
 	va_list args;
 	va_start(args, fmt);
-	ret = vsnpath(buf, n, fmt, args);
+	do_git_path(sb, fmt, args);
 	va_end(args);
-	return ret;
+}
+
+const char *git_path(const char *fmt, ...)
+{
+	struct strbuf *pathname = get_pathname();
+	va_list args;
+	va_start(args, fmt);
+	do_git_path(pathname, fmt, args);
+	va_end(args);
+	return pathname->buf;
 }
 
 char *git_pathdup(const char *fmt, ...)
 {
-	char path[PATH_MAX], *ret;
+	struct strbuf path = STRBUF_INIT;
 	va_list args;
 	va_start(args, fmt);
-	ret = vsnpath(path, sizeof(path), fmt, args);
+	do_git_path(&path, fmt, args);
 	va_end(args);
-	return xstrdup(ret);
+	return strbuf_detach(&path, NULL);
 }
 
 char *mkpathdup(const char *fmt, ...)
 {
-	char *path;
 	struct strbuf sb = STRBUF_INIT;
 	va_list args;
-
 	va_start(args, fmt);
 	strbuf_vaddf(&sb, fmt, args);
 	va_end(args);
-	path = xstrdup(cleanup_path(sb.buf));
-
-	strbuf_release(&sb);
-	return path;
+	strbuf_cleanup_path(&sb);
+	return strbuf_detach(&sb, NULL);
 }
 
-char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
 {
 	va_list args;
-	unsigned len;
-	char *pathname = get_pathname();
-
+	struct strbuf *pathname = get_pathname();
 	va_start(args, fmt);
-	len = vsnprintf(pathname, PATH_MAX, fmt, args);
+	strbuf_vaddf(pathname, fmt, args);
 	va_end(args);
-	if (len >= PATH_MAX)
-		return bad_path;
-	return cleanup_path(pathname);
+	return cleanup_path(pathname->buf);
 }
 
-char *git_path(const char *fmt, ...)
+const char *git_path_submodule(const char *path, const char *fmt, ...)
 {
-	char *pathname = get_pathname();
-	va_list args;
-	char *ret;
-
-	va_start(args, fmt);
-	ret = vsnpath(pathname, PATH_MAX, fmt, args);
-	va_end(args);
-	return ret;
-}
-
-char *git_path_submodule(const char *path, const char *fmt, ...)
-{
-	char *pathname = get_pathname();
-	struct strbuf buf = STRBUF_INIT;
+	struct strbuf *buf = get_pathname();
 	const char *git_dir;
 	va_list args;
-	unsigned len;
 
-	len = strlen(path);
-	if (len > PATH_MAX-100)
-		return bad_path;
+	strbuf_addstr(buf, path);
+	if (buf->len && buf->buf[buf->len - 1] != '/')
+		strbuf_addch(buf, '/');
+	strbuf_addstr(buf, ".git");
 
-	strbuf_addstr(&buf, path);
-	if (len && path[len-1] != '/')
-		strbuf_addch(&buf, '/');
-	strbuf_addstr(&buf, ".git");
-
-	git_dir = read_gitfile(buf.buf);
+	git_dir = read_gitfile(buf->buf);
 	if (git_dir) {
-		strbuf_reset(&buf);
-		strbuf_addstr(&buf, git_dir);
+		strbuf_reset(buf);
+		strbuf_addstr(buf, git_dir);
 	}
-	strbuf_addch(&buf, '/');
-
-	if (buf.len >= PATH_MAX)
-		return bad_path;
-	memcpy(pathname, buf.buf, buf.len + 1);
-
-	strbuf_release(&buf);
-	len = strlen(pathname);
+	strbuf_addch(buf, '/');
 
 	va_start(args, fmt);
-	len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+	strbuf_vaddf(buf, fmt, args);
 	va_end(args);
-	if (len >= PATH_MAX)
-		return bad_path;
-	return cleanup_path(pathname);
+	strbuf_cleanup_path(buf);
+	return buf->buf;
 }
 
 int validate_headref(const char *path)
diff --git a/progress.c b/progress.c
index 412e6b1..2e31bec 100644
--- a/progress.c
+++ b/progress.c
@@ -72,6 +72,12 @@
 	progress_update = 0;
 }
 
+static int is_foreground_fd(int fd)
+{
+	int tpgrp = tcgetpgrp(fd);
+	return tpgrp < 0 || tpgrp == getpgid(0);
+}
+
 static int display(struct progress *progress, unsigned n, const char *done)
 {
 	const char *eol, *tp;
@@ -98,16 +104,21 @@
 		unsigned percent = n * 100 / progress->total;
 		if (percent != progress->last_percent || progress_update) {
 			progress->last_percent = percent;
-			fprintf(stderr, "%s: %3u%% (%u/%u)%s%s",
-				progress->title, percent, n,
-				progress->total, tp, eol);
-			fflush(stderr);
+			if (is_foreground_fd(fileno(stderr)) || done) {
+				fprintf(stderr, "%s: %3u%% (%u/%u)%s%s",
+					progress->title, percent, n,
+					progress->total, tp, eol);
+				fflush(stderr);
+			}
 			progress_update = 0;
 			return 1;
 		}
 	} else if (progress_update) {
-		fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol);
-		fflush(stderr);
+		if (is_foreground_fd(fileno(stderr)) || done) {
+			fprintf(stderr, "%s: %u%s%s",
+				progress->title, n, tp, eol);
+			fflush(stderr);
+		}
 		progress_update = 0;
 		return 1;
 	}
diff --git a/reachable.c b/reachable.c
index 69fa685..9cff25b 100644
--- a/reachable.c
+++ b/reachable.c
@@ -22,9 +22,10 @@
 		display_progress(cp->progress, cp->count);
 }
 
-static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_one_ref(const char *path, const struct object_id *oid,
+		       int flag, void *cb_data)
 {
-	struct object *object = parse_object_or_die(sha1, path);
+	struct object *object = parse_object_or_die(oid->hash, path);
 	struct rev_info *revs = (struct rev_info *)cb_data;
 
 	add_pending_object(revs, object, "");
diff --git a/read-cache.c b/read-cache.c
index bf32270..89dbc08 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -39,11 +39,12 @@
 #define CACHE_EXT_TREE 0x54524545	/* "TREE" */
 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
 #define CACHE_EXT_LINK 0x6c696e6b	  /* "link" */
+#define CACHE_EXT_UNTRACKED 0x554E5452	  /* "UNTR" */
 
 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
 		 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
-		 SPLIT_INDEX_ORDERED)
+		 SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
 
 struct index_state the_index;
 static const char *alternate_index_output;
@@ -79,6 +80,7 @@
 	memcpy(new->name, new_name, namelen + 1);
 
 	cache_tree_invalidate_path(istate, old->name);
+	untracked_cache_remove_from_index(istate, old->name);
 	remove_index_entry_at(istate, nr);
 	add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
@@ -270,20 +272,34 @@
 	return changed;
 }
 
+static int is_racy_stat(const struct index_state *istate,
+			const struct stat_data *sd)
+{
+	return (istate->timestamp.sec &&
+#ifdef USE_NSEC
+		 /* nanosecond timestamped files can also be racy! */
+		(istate->timestamp.sec < sd->sd_mtime.sec ||
+		 (istate->timestamp.sec == sd->sd_mtime.sec &&
+		  istate->timestamp.nsec <= sd->sd_mtime.nsec))
+#else
+		istate->timestamp.sec <= sd->sd_mtime.sec
+#endif
+		);
+}
+
 static int is_racy_timestamp(const struct index_state *istate,
 			     const struct cache_entry *ce)
 {
 	return (!S_ISGITLINK(ce->ce_mode) &&
-		istate->timestamp.sec &&
-#ifdef USE_NSEC
-		 /* nanosecond timestamped files can also be racy! */
-		(istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
-		 (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
-		  istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
-#else
-		istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
-#endif
-		 );
+		is_racy_stat(istate, &ce->ce_stat_data));
+}
+
+int match_stat_data_racy(const struct index_state *istate,
+			 const struct stat_data *sd, struct stat *st)
+{
+	if (is_racy_stat(istate, sd))
+		return MTIME_CHANGED;
+	return match_stat_data(sd, st);
 }
 
 int ie_match_stat(const struct index_state *istate,
@@ -538,6 +554,7 @@
 	if (pos < 0)
 		pos = -pos-1;
 	cache_tree_invalidate_path(istate, path);
+	untracked_cache_remove_from_index(istate, path);
 	while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
 		remove_index_entry_at(istate, pos);
 	return 0;
@@ -982,6 +999,9 @@
 	}
 	pos = -pos-1;
 
+	if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
+		untracked_cache_add_to_index(istate, ce->name);
+
 	/*
 	 * Inserting a merged entry ("stage 0") into the index
 	 * will always replace all non-merged entries..
@@ -1372,6 +1392,9 @@
 		if (read_link_extension(istate, data, sz))
 			return -1;
 		break;
+	case CACHE_EXT_UNTRACKED:
+		istate->untracked = read_untracked_extension(data, sz);
+		break;
 	default:
 		if (*ext < 'A' || 'Z' < *ext)
 			return error("index uses %.4s extension, which we do not understand",
@@ -1667,6 +1690,8 @@
 	istate->cache = NULL;
 	istate->cache_alloc = 0;
 	discard_split_index(istate);
+	free_untracked_cache(istate->untracked);
+	istate->untracked = NULL;
 	return 0;
 }
 
@@ -2053,6 +2078,17 @@
 		if (err)
 			return -1;
 	}
+	if (!strip_extensions && istate->untracked) {
+		struct strbuf sb = STRBUF_INIT;
+
+		write_untracked_extension(&sb, istate->untracked);
+		err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED,
+					     sb.len) < 0 ||
+			ce_write(&c, newfd, sb.buf, sb.len) < 0;
+		strbuf_release(&sb);
+		if (err)
+			return -1;
+	}
 
 	if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
 		return -1;
diff --git a/refs.c b/refs.c
index 67d6745..7ac05cf 100644
--- a/refs.c
+++ b/refs.c
@@ -10,8 +10,7 @@
 	char *ref_name;
 	char *orig_ref_name;
 	struct lock_file *lk;
-	unsigned char old_sha1[20];
-	int lock_fd;
+	struct object_id old_oid;
 };
 
 /*
@@ -162,7 +161,7 @@
 	 * null.  If REF_ISSYMREF, then this is the name of the object
 	 * referred to by the last reference in the symlink chain.
 	 */
-	unsigned char sha1[20];
+	struct object_id oid;
 
 	/*
 	 * If REF_KNOWS_PEELED, then this field holds the peeled value
@@ -170,7 +169,7 @@
 	 * be peelable.  See the documentation for peel_ref() for an
 	 * exact definition of "peelable".
 	 */
-	unsigned char peeled[20];
+	struct object_id peeled;
 };
 
 struct ref_cache;
@@ -350,12 +349,10 @@
 	if (check_name &&
 	    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 		die("Reference has invalid format: '%s'", refname);
-	if (!check_name && !refname_is_safe(refname))
-		die("Reference has invalid name: '%s'", refname);
 	len = strlen(refname) + 1;
 	ref = xmalloc(sizeof(struct ref_entry) + len);
-	hashcpy(ref->u.value.sha1, sha1);
-	hashclr(ref->u.value.peeled);
+	hashcpy(ref->u.value.oid.hash, sha1);
+	oidclr(&ref->u.value.peeled);
 	memcpy(ref->name, refname, len);
 	ref->flag = flag;
 	return ref;
@@ -629,7 +626,7 @@
 		/* This is impossible by construction */
 		die("Reference directory conflict: %s", ref1->name);
 
-	if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
+	if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
 		die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
 
 	warning("Duplicated ref: %s", ref1->name);
@@ -677,7 +674,7 @@
 {
 	if (entry->flag & REF_ISBROKEN)
 		return 0;
-	if (!has_sha1_file(entry->u.value.sha1)) {
+	if (!has_sha1_file(entry->u.value.oid.hash)) {
 		error("%s does not point to a valid object!", entry->name);
 		return 0;
 	}
@@ -725,7 +722,7 @@
 	/* Store the old value, in case this is a recursive call: */
 	old_current_ref = current_ref;
 	current_ref = entry;
-	retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
+	retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
 			  entry->flag, data->cb_data);
 	current_ref = old_current_ref;
 	return retval;
@@ -1244,6 +1241,8 @@
 			int flag = REF_ISPACKED;
 
 			if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+				if (!refname_is_safe(refname))
+					die("packed refname is dangerous: %s", refname);
 				hashclr(sha1);
 				flag |= REF_BAD_NAME | REF_ISBROKEN;
 			}
@@ -1259,7 +1258,7 @@
 		    line.len == PEELED_LINE_LENGTH &&
 		    line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
 		    !get_sha1_hex(line.buf + 1, sha1)) {
-			hashcpy(last->u.value.peeled, sha1);
+			hashcpy(last->u.value.peeled.hash, sha1);
 			/*
 			 * Regardless of what the file header said,
 			 * we definitely know the value of *this*
@@ -1374,21 +1373,38 @@
 					 create_dir_entry(refs, refname.buf,
 							  refname.len, 1));
 		} else {
+			int read_ok;
+
 			if (*refs->name) {
 				hashclr(sha1);
 				flag = 0;
-				if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
-					hashclr(sha1);
-					flag |= REF_ISBROKEN;
-				}
-			} else if (read_ref_full(refname.buf,
-						 RESOLVE_REF_READING,
-						 sha1, &flag)) {
+				read_ok = !resolve_gitlink_ref(refs->name,
+							       refname.buf, sha1);
+			} else {
+				read_ok = !read_ref_full(refname.buf,
+							 RESOLVE_REF_READING,
+							 sha1, &flag);
+			}
+
+			if (!read_ok) {
 				hashclr(sha1);
 				flag |= REF_ISBROKEN;
+			} else if (is_null_sha1(sha1)) {
+				/*
+				 * It is so astronomically unlikely
+				 * that NULL_SHA1 is the SHA-1 of an
+				 * actual object that we consider its
+				 * appearance in a loose reference
+				 * file to be repo corruption
+				 * (probably due to a software bug).
+				 */
+				flag |= REF_ISBROKEN;
 			}
+
 			if (check_refname_format(refname.buf,
 						 REFNAME_ALLOW_ONELEVEL)) {
+				if (!refname_is_safe(refname.buf))
+					die("loose refname is dangerous: %s", refname.buf);
 				hashclr(sha1);
 				flag |= REF_BAD_NAME | REF_ISBROKEN;
 			}
@@ -1438,7 +1454,7 @@
 	if (ref == NULL)
 		return -1;
 
-	hashcpy(sha1, ref->u.value.sha1);
+	hashcpy(sha1, ref->u.value.oid.hash);
 	return 0;
 }
 
@@ -1448,7 +1464,7 @@
 {
 	int fd, len;
 	char buffer[128], *p;
-	char *path;
+	const char *path;
 
 	if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
 		return -1;
@@ -1525,7 +1541,7 @@
 	 */
 	entry = get_packed_ref(refname);
 	if (entry) {
-		hashcpy(sha1, entry->u.value.sha1);
+		hashcpy(sha1, entry->u.value.oid.hash);
 		if (flags)
 			*flags |= REF_ISPACKED;
 		return 0;
@@ -1541,7 +1557,11 @@
 }
 
 /* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+static const char *resolve_ref_unsafe_1(const char *refname,
+					int resolve_flags,
+					unsigned char *sha1,
+					int *flags,
+					struct strbuf *sb_path)
 {
 	int depth = MAXDEPTH;
 	ssize_t len;
@@ -1572,7 +1592,7 @@
 		bad_name = 1;
 	}
 	for (;;) {
-		char path[PATH_MAX];
+		const char *path;
 		struct stat st;
 		char *buf;
 		int fd;
@@ -1582,7 +1602,9 @@
 			return NULL;
 		}
 
-		git_snpath(path, sizeof(path), "%s", refname);
+		strbuf_reset(sb_path);
+		strbuf_git_path(sb_path, "%s", refname);
+		path = sb_path->buf;
 
 		/*
 		 * We might have to loop back here to avoid a race
@@ -1709,6 +1731,16 @@
 	}
 }
 
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+			       unsigned char *sha1, int *flags)
+{
+	struct strbuf sb_path = STRBUF_INIT;
+	const char *ret = resolve_ref_unsafe_1(refname, resolve_flags,
+					       sha1, flags, &sb_path);
+	strbuf_release(&sb_path);
+	return ret;
+}
+
 char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
 {
 	return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
@@ -1739,13 +1771,14 @@
 	return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
 }
 
-static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
-		       void *data)
+static int filter_refs(const char *refname, const struct object_id *oid,
+			   int flags, void *data)
 {
 	struct ref_filter *filter = (struct ref_filter *)data;
+
 	if (wildmatch(filter->pattern, refname, 0, NULL))
 		return 0;
-	return filter->fn(refname, sha1, flags, filter->cb_data);
+	return filter->fn(refname, oid, flags, filter->cb_data);
 }
 
 enum peel_status {
@@ -1819,9 +1852,9 @@
 	if (entry->flag & REF_KNOWS_PEELED) {
 		if (repeel) {
 			entry->flag &= ~REF_KNOWS_PEELED;
-			hashclr(entry->u.value.peeled);
+			oidclr(&entry->u.value.peeled);
 		} else {
-			return is_null_sha1(entry->u.value.peeled) ?
+			return is_null_oid(&entry->u.value.peeled) ?
 				PEEL_NON_TAG : PEEL_PEELED;
 		}
 	}
@@ -1830,7 +1863,7 @@
 	if (entry->flag & REF_ISSYMREF)
 		return PEEL_IS_SYMREF;
 
-	status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
+	status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
 	if (status == PEEL_PEELED || status == PEEL_NON_TAG)
 		entry->flag |= REF_KNOWS_PEELED;
 	return status;
@@ -1845,7 +1878,7 @@
 			    || !strcmp(current_ref->name, refname))) {
 		if (peel_entry(current_ref, 0))
 			return -1;
-		hashcpy(sha1, current_ref->u.value.peeled);
+		hashcpy(sha1, current_ref->u.value.peeled.hash);
 		return 0;
 	}
 
@@ -1865,7 +1898,7 @@
 		if (r) {
 			if (peel_entry(r, 0))
 				return -1;
-			hashcpy(sha1, r->u.value.peeled);
+			hashcpy(sha1, r->u.value.peeled.hash);
 			return 0;
 		}
 	}
@@ -1880,17 +1913,17 @@
 	const char *msg_fmt;
 };
 
-static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
 				   int flags, void *cb_data)
 {
 	struct warn_if_dangling_data *d = cb_data;
 	const char *resolves_to;
-	unsigned char junk[20];
+	struct object_id junk;
 
 	if (!(flags & REF_ISSYMREF))
 		return 0;
 
-	resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
+	resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
 	if (!resolves_to
 	    || (d->refname
 		? strcmp(resolves_to, d->refname)
@@ -2010,18 +2043,18 @@
 
 static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-	unsigned char sha1[20];
+	struct object_id oid;
 	int flag;
 
 	if (submodule) {
-		if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
-			return fn("HEAD", sha1, 0, cb_data);
+		if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+			return fn("HEAD", &oid, 0, cb_data);
 
 		return 0;
 	}
 
-	if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
-		return fn("HEAD", sha1, flag, cb_data);
+	if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+		return fn("HEAD", &oid, flag, cb_data);
 
 	return 0;
 }
@@ -2096,12 +2129,12 @@
 {
 	struct strbuf buf = STRBUF_INIT;
 	int ret = 0;
-	unsigned char sha1[20];
+	struct object_id oid;
 	int flag;
 
 	strbuf_addf(&buf, "%sHEAD", get_git_namespace());
-	if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
-		ret = fn(buf.buf, sha1, flag, cb_data);
+	if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
+		ret = fn(buf.buf, &oid, flag, cb_data);
 	strbuf_release(&buf);
 
 	return ret;
@@ -2201,27 +2234,35 @@
 	free(lock);
 }
 
-/* This function should make sure errno is meaningful on error */
-static struct ref_lock *verify_lock(struct ref_lock *lock,
-	const unsigned char *old_sha1, int mustexist)
+/*
+ * Verify that the reference locked by lock has the value old_sha1.
+ * Fail if the reference doesn't exist and mustexist is set. Return 0
+ * on success. On error, write an error message to err, set errno, and
+ * return a negative value.
+ */
+static int verify_lock(struct ref_lock *lock,
+		       const unsigned char *old_sha1, int mustexist,
+		       struct strbuf *err)
 {
+	assert(err);
+
 	if (read_ref_full(lock->ref_name,
 			  mustexist ? RESOLVE_REF_READING : 0,
-			  lock->old_sha1, NULL)) {
+			  lock->old_oid.hash, NULL)) {
 		int save_errno = errno;
-		error("Can't verify ref %s", lock->ref_name);
-		unlock_ref(lock);
+		strbuf_addf(err, "can't verify ref %s", lock->ref_name);
 		errno = save_errno;
-		return NULL;
+		return -1;
 	}
-	if (hashcmp(lock->old_sha1, old_sha1)) {
-		error("Ref %s is at %s but expected %s", lock->ref_name,
-			sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
-		unlock_ref(lock);
+	if (hashcmp(lock->old_oid.hash, old_sha1)) {
+		strbuf_addf(err, "ref %s is at %s but expected %s",
+			    lock->ref_name,
+			    sha1_to_hex(lock->old_oid.hash),
+			    sha1_to_hex(old_sha1));
 		errno = EBUSY;
-		return NULL;
+		return -1;
 	}
-	return lock;
+	return 0;
 }
 
 static int remove_empty_directories(const char *file)
@@ -2342,7 +2383,7 @@
 					    unsigned int flags, int *type_p,
 					    struct strbuf *err)
 {
-	char *ref_file;
+	const char *ref_file;
 	const char *orig_refname = refname;
 	struct ref_lock *lock;
 	int last_errno = 0;
@@ -2354,7 +2395,6 @@
 	assert(err);
 
 	lock = xcalloc(1, sizeof(struct ref_lock));
-	lock->lock_fd = -1;
 
 	if (mustexist)
 		resolve_flags |= RESOLVE_REF_READING;
@@ -2365,7 +2405,7 @@
 	}
 
 	refname = resolve_ref_unsafe(refname, resolve_flags,
-				     lock->old_sha1, &type);
+				     lock->old_oid.hash, &type);
 	if (!refname && errno == EISDIR) {
 		/* we are trying to lock foo but we used to
 		 * have foo/bar which now does not exist;
@@ -2384,7 +2424,7 @@
 			goto error_return;
 		}
 		refname = resolve_ref_unsafe(orig_refname, resolve_flags,
-					     lock->old_sha1, &type);
+					     lock->old_oid.hash, &type);
 	}
 	if (type_p)
 	    *type_p = type;
@@ -2404,7 +2444,7 @@
 	 * refname, nor a packed ref whose name is a proper prefix of
 	 * our refname.
 	 */
-	if (is_null_sha1(lock->old_sha1) &&
+	if (is_null_oid(&lock->old_oid) &&
 	    verify_refname_available(refname, extras, skip,
 				     get_packed_refs(&ref_cache), err)) {
 		last_errno = ENOTDIR;
@@ -2423,7 +2463,7 @@
 	ref_file = git_path("%s", refname);
 
  retry:
-	switch (safe_create_leading_directories(ref_file)) {
+	switch (safe_create_leading_directories_const(ref_file)) {
 	case SCLD_OK:
 		break; /* success */
 	case SCLD_VANISHED:
@@ -2436,8 +2476,7 @@
 		goto error_return;
 	}
 
-	lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
-	if (lock->lock_fd < 0) {
+	if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
 		last_errno = errno;
 		if (errno == ENOENT && --attempts_remaining > 0)
 			/*
@@ -2451,7 +2490,11 @@
 			goto error_return;
 		}
 	}
-	return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+	if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
+		last_errno = errno;
+		goto error_return;
+	}
+	return lock;
 
  error_return:
 	unlock_ref(lock);
@@ -2481,18 +2524,28 @@
 	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
 		error("internal error: %s is not a valid packed reference!",
 		      entry->name);
-	write_packed_entry(cb_data, entry->name, entry->u.value.sha1,
+	write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
 			   peel_status == PEEL_PEELED ?
-			   entry->u.value.peeled : NULL);
+			   entry->u.value.peeled.hash : NULL);
 	return 0;
 }
 
 /* This should return a meaningful errno on failure */
 int lock_packed_refs(int flags)
 {
+	static int timeout_configured = 0;
+	static int timeout_value = 1000;
+
 	struct packed_ref_cache *packed_ref_cache;
 
-	if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+	if (!timeout_configured) {
+		git_config_get_int("core.packedrefstimeout", &timeout_value);
+		timeout_configured = 1;
+	}
+
+	if (hold_lock_file_for_update_timeout(
+			    &packlock, git_path("packed-refs"),
+			    flags, timeout_value) < 0)
 		return -1;
 	/*
 	 * Get the current packed-refs while holding the lock.  If the
@@ -2590,24 +2643,24 @@
 	peel_status = peel_entry(entry, 1);
 	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
 		die("internal error peeling reference %s (%s)",
-		    entry->name, sha1_to_hex(entry->u.value.sha1));
+		    entry->name, oid_to_hex(&entry->u.value.oid));
 	packed_entry = find_ref(cb->packed_refs, entry->name);
 	if (packed_entry) {
 		/* Overwrite existing packed entry with info from loose entry */
 		packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
-		hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+		oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
 	} else {
-		packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+		packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
 						REF_ISPACKED | REF_KNOWS_PEELED, 0);
 		add_ref(cb->packed_refs, packed_entry);
 	}
-	hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
+	oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
 
 	/* Schedule the loose reference for pruning if requested. */
 	if ((cb->flags & PACK_REFS_PRUNE)) {
 		int namelen = strlen(entry->name) + 1;
 		struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
-		hashcpy(n->sha1, entry->u.value.sha1);
+		hashcpy(n->sha1, entry->u.value.oid.hash);
 		strcpy(n->name, entry->name);
 		n->next = cb->ref_to_prune;
 		cb->ref_to_prune = n;
@@ -2798,7 +2851,7 @@
 	int attempts_remaining = 4;
 
  retry:
-	switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+	switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
 	case SCLD_OK:
 		break; /* success */
 	case SCLD_VANISHED:
@@ -2918,7 +2971,7 @@
 		strbuf_release(&err);
 		goto rollback;
 	}
-	hashcpy(lock->old_sha1, orig_sha1);
+	hashcpy(lock->old_oid.hash, orig_sha1);
 
 	if (write_ref_to_lockfile(lock, orig_sha1) ||
 	    commit_ref_update(lock, orig_sha1, logmsg)) {
@@ -2959,7 +3012,6 @@
 {
 	if (close_lock_file(lock->lk))
 		return -1;
-	lock->lock_fd = -1;
 	return 0;
 }
 
@@ -2967,7 +3019,6 @@
 {
 	if (commit_lock_file(lock->lk))
 		return -1;
-	lock->lock_fd = -1;
 	return 0;
 }
 
@@ -2998,11 +3049,15 @@
 }
 
 /* This function must set a meaningful errno on failure */
-int log_ref_setup(const char *refname, char *logfile, int bufsize)
+int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
 {
 	int logfd, oflags = O_APPEND | O_WRONLY;
+	char *logfile;
 
-	git_snpath(logfile, bufsize, "logs/%s", refname);
+	strbuf_git_path(sb_logfile, "logs/%s", refname);
+	logfile = sb_logfile->buf;
+	/* make sure the rest of the function can't change "logfile" */
+	sb_logfile = NULL;
 	if (log_all_ref_updates &&
 	    (starts_with(refname, "refs/heads/") ||
 	     starts_with(refname, "refs/remotes/") ||
@@ -3073,18 +3128,22 @@
 	return 0;
 }
 
-static int log_ref_write(const char *refname, const unsigned char *old_sha1,
-			 const unsigned char *new_sha1, const char *msg)
+static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
+			   const unsigned char *new_sha1, const char *msg,
+			   struct strbuf *sb_log_file)
 {
 	int logfd, result, oflags = O_APPEND | O_WRONLY;
-	char log_file[PATH_MAX];
+	char *log_file;
 
 	if (log_all_ref_updates < 0)
 		log_all_ref_updates = !is_bare_repository();
 
-	result = log_ref_setup(refname, log_file, sizeof(log_file));
+	result = log_ref_setup(refname, sb_log_file);
 	if (result)
 		return result;
+	log_file = sb_log_file->buf;
+	/* make sure the rest of the function can't change "log_file" */
+	sb_log_file = NULL;
 
 	logfd = open(log_file, oflags);
 	if (logfd < 0)
@@ -3107,6 +3166,15 @@
 	return 0;
 }
 
+static int log_ref_write(const char *refname, const unsigned char *old_sha1,
+			 const unsigned char *new_sha1, const char *msg)
+{
+	struct strbuf sb = STRBUF_INIT;
+	int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
+	strbuf_release(&sb);
+	return ret;
+}
+
 int is_branch(const char *refname)
 {
 	return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
@@ -3137,8 +3205,8 @@
 		errno = EINVAL;
 		return -1;
 	}
-	if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
-	    write_in_full(lock->lock_fd, &term, 1) != 1 ||
+	if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
+	    write_in_full(lock->lk->fd, &term, 1) != 1 ||
 	    close_ref(lock) < 0) {
 		int save_errno = errno;
 		error("Couldn't write %s", lock->lk->filename.buf);
@@ -3158,9 +3226,9 @@
 			     const unsigned char *sha1, const char *logmsg)
 {
 	clear_loose_ref_cache(&ref_cache);
-	if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
+	if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
 	    (strcmp(lock->ref_name, lock->orig_ref_name) &&
-	     log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
+	     log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
 		unlock_ref(lock);
 		return -1;
 	}
@@ -3184,7 +3252,7 @@
 					      head_sha1, &head_flag);
 		if (head_ref && (head_flag & REF_ISSYMREF) &&
 		    !strcmp(head_ref, lock->ref_name))
-			log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+			log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
 	}
 	if (commit_ref(lock)) {
 		error("Couldn't set %s", lock->ref_name);
@@ -3576,11 +3644,12 @@
 				strbuf_addch(name, '/');
 				retval = do_for_each_reflog(name, fn, cb_data);
 			} else {
-				unsigned char sha1[20];
-				if (read_ref_full(name->buf, 0, sha1, NULL))
+				struct object_id oid;
+
+				if (read_ref_full(name->buf, 0, oid.hash, NULL))
 					retval = error("bad ref for %s", name->buf);
 				else
-					retval = fn(name->buf, sha1, 0, cb_data);
+					retval = fn(name->buf, &oid, 0, cb_data);
 			}
 			if (retval)
 				break;
@@ -3870,7 +3939,7 @@
 				? TRANSACTION_NAME_CONFLICT
 				: TRANSACTION_GENERIC_ERROR;
 			reason = strbuf_detach(err, NULL);
-			strbuf_addf(err, "Cannot lock ref '%s': %s",
+			strbuf_addf(err, "cannot lock ref '%s': %s",
 				    update->refname, reason);
 			free(reason);
 			goto cleanup;
@@ -3881,7 +3950,7 @@
 						  (update->flags & REF_NODEREF));
 
 			if (!overwriting_symref &&
-			    !hashcmp(update->lock->old_sha1, update->new_sha1)) {
+			    !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
 				/*
 				 * The reference already has the desired
 				 * value, so we don't need to write it.
@@ -3893,7 +3962,7 @@
 				 * write_ref_to_lockfile():
 				 */
 				update->lock = NULL;
-				strbuf_addf(err, "Cannot update the ref '%s'.",
+				strbuf_addf(err, "cannot update the ref '%s'.",
 					    update->refname);
 				ret = TRANSACTION_GENERIC_ERROR;
 				goto cleanup;
@@ -4222,9 +4291,9 @@
 			status |= error("couldn't write %s: %s", log_file,
 					strerror(errno));
 		} else if (update &&
-			(write_in_full(lock->lock_fd,
+			   (write_in_full(lock->lk->fd,
 				sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
-			 write_str_in_full(lock->lock_fd, "\n") != 1 ||
+			 write_str_in_full(lock->lk->fd, "\n") != 1 ||
 			 close_ref(lock) < 0)) {
 			status |= error("couldn't write %s",
 					lock->lk->filename.buf);
diff --git a/refs.h b/refs.h
index cf642e6..8c3d433 100644
--- a/refs.h
+++ b/refs.h
@@ -67,7 +67,7 @@
  * single callback invocation.
  */
 typedef int each_ref_fn(const char *refname,
-			const unsigned char *sha1, int flags, void *cb_data);
+			const struct object_id *oid, int flags, void *cb_data);
 
 /*
  * The following functions invoke the specified callback function for
@@ -191,7 +191,7 @@
 /*
  * Setup reflog before using. Set errno to something meaningful on failure.
  */
-int log_ref_setup(const char *refname, char *logfile, int bufsize);
+int log_ref_setup(const char *refname, struct strbuf *logfile);
 
 /** Reads log for the value of ref during at_time. **/
 extern int read_ref_at(const char *refname, unsigned int flags,
diff --git a/remote.c b/remote.c
index 68901b0..26504b7 100644
--- a/remote.c
+++ b/remote.c
@@ -49,10 +49,7 @@
 static int branches_nr;
 
 static struct branch *current_branch;
-static const char *default_remote_name;
-static const char *branch_pushremote_name;
 static const char *pushremote_name;
-static int explicit_default_remote_name;
 
 static struct rewrites rewrites;
 static struct rewrites rewrites_push;
@@ -367,16 +364,9 @@
 			return 0;
 		branch = make_branch(name, subkey - name);
 		if (!strcmp(subkey, ".remote")) {
-			if (git_config_string(&branch->remote_name, key, value))
-				return -1;
-			if (branch == current_branch) {
-				default_remote_name = branch->remote_name;
-				explicit_default_remote_name = 1;
-			}
+			return git_config_string(&branch->remote_name, key, value);
 		} else if (!strcmp(subkey, ".pushremote")) {
-			if (branch == current_branch)
-				if (git_config_string(&branch_pushremote_name, key, value))
-					return -1;
+			return git_config_string(&branch->pushremote_name, key, value);
 		} else if (!strcmp(subkey, ".merge")) {
 			if (!value)
 				return config_error_nonbool(key);
@@ -501,12 +491,15 @@
 
 static void read_config(void)
 {
+	static int loaded;
 	unsigned char sha1[20];
 	const char *head_ref;
 	int flag;
-	if (default_remote_name) /* did this already */
+
+	if (loaded)
 		return;
-	default_remote_name = "origin";
+	loaded = 1;
+
 	current_branch = NULL;
 	head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
 	if (head_ref && (flag & REF_ISSYMREF) &&
@@ -514,10 +507,6 @@
 		current_branch = make_branch(head_ref, 0);
 	}
 	git_config(handle_config, NULL);
-	if (branch_pushremote_name) {
-		free((char *)pushremote_name);
-		pushremote_name = branch_pushremote_name;
-	}
 	alias_all_urls();
 }
 
@@ -696,22 +685,45 @@
 	return !strchr(name, '/'); /* no slash */
 }
 
-static struct remote *remote_get_1(const char *name, const char *pushremote_name)
+const char *remote_for_branch(struct branch *branch, int *explicit)
+{
+	if (branch && branch->remote_name) {
+		if (explicit)
+			*explicit = 1;
+		return branch->remote_name;
+	}
+	if (explicit)
+		*explicit = 0;
+	return "origin";
+}
+
+const char *pushremote_for_branch(struct branch *branch, int *explicit)
+{
+	if (branch && branch->pushremote_name) {
+		if (explicit)
+			*explicit = 1;
+		return branch->pushremote_name;
+	}
+	if (pushremote_name) {
+		if (explicit)
+			*explicit = 1;
+		return pushremote_name;
+	}
+	return remote_for_branch(branch, explicit);
+}
+
+static struct remote *remote_get_1(const char *name,
+				   const char *(*get_default)(struct branch *, int *))
 {
 	struct remote *ret;
 	int name_given = 0;
 
+	read_config();
+
 	if (name)
 		name_given = 1;
-	else {
-		if (pushremote_name) {
-			name = pushremote_name;
-			name_given = 1;
-		} else {
-			name = default_remote_name;
-			name_given = explicit_default_remote_name;
-		}
-	}
+	else
+		name = get_default(current_branch, &name_given);
 
 	ret = make_remote(name, 0);
 	if (valid_remote_nick(name)) {
@@ -731,14 +743,12 @@
 
 struct remote *remote_get(const char *name)
 {
-	read_config();
-	return remote_get_1(name, NULL);
+	return remote_get_1(name, remote_for_branch);
 }
 
 struct remote *pushremote_get(const char *name)
 {
-	read_config();
-	return remote_get_1(name, pushremote_name);
+	return remote_get_1(name, pushremote_for_branch);
 }
 
 int remote_is_configured(const char *name)
@@ -1633,15 +1643,31 @@
 
 static void set_merge(struct branch *ret)
 {
+	struct remote *remote;
 	char *ref;
 	unsigned char sha1[20];
 	int i;
 
+	if (!ret)
+		return; /* no branch */
+	if (ret->merge)
+		return; /* already run */
+	if (!ret->remote_name || !ret->merge_nr) {
+		/*
+		 * no merge config; let's make sure we don't confuse callers
+		 * with a non-zero merge_nr but a NULL merge
+		 */
+		ret->merge_nr = 0;
+		return;
+	}
+
+	remote = remote_get(ret->remote_name);
+
 	ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
 	for (i = 0; i < ret->merge_nr; i++) {
 		ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
 		ret->merge[i]->src = xstrdup(ret->merge_name[i]);
-		if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
+		if (!remote_find_tracking(remote, ret->merge[i]) ||
 		    strcmp(ret->remote_name, "."))
 			continue;
 		if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
@@ -1661,11 +1687,7 @@
 		ret = current_branch;
 	else
 		ret = make_branch(name, 0);
-	if (ret && ret->remote_name) {
-		ret->remote = remote_get(ret->remote_name);
-		if (ret->merge_nr)
-			set_merge(ret);
-	}
+	set_merge(ret);
 	return ret;
 }
 
@@ -1683,6 +1705,130 @@
 	return refname_match(branch->merge[i]->src, refname);
 }
 
+__attribute((format (printf,2,3)))
+static const char *error_buf(struct strbuf *err, const char *fmt, ...)
+{
+	if (err) {
+		va_list ap;
+		va_start(ap, fmt);
+		strbuf_vaddf(err, fmt, ap);
+		va_end(ap);
+	}
+	return NULL;
+}
+
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
+{
+	if (!branch)
+		return error_buf(err, _("HEAD does not point to a branch"));
+
+	if (!branch->merge || !branch->merge[0]) {
+		/*
+		 * no merge config; is it because the user didn't define any,
+		 * or because it is not a real branch, and get_branch
+		 * auto-vivified it?
+		 */
+		if (!ref_exists(branch->refname))
+			return error_buf(err, _("no such branch: '%s'"),
+					 branch->name);
+		return error_buf(err,
+				 _("no upstream configured for branch '%s'"),
+				 branch->name);
+	}
+
+	if (!branch->merge[0]->dst)
+		return error_buf(err,
+				 _("upstream branch '%s' not stored as a remote-tracking branch"),
+				 branch->merge[0]->src);
+
+	return branch->merge[0]->dst;
+}
+
+static const char *tracking_for_push_dest(struct remote *remote,
+					  const char *refname,
+					  struct strbuf *err)
+{
+	char *ret;
+
+	ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+	if (!ret)
+		return error_buf(err,
+				 _("push destination '%s' on remote '%s' has no local tracking branch"),
+				 refname, remote->name);
+	return ret;
+}
+
+static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
+{
+	struct remote *remote;
+
+	if (!branch)
+		return error_buf(err, _("HEAD does not point to a branch"));
+
+	remote = remote_get(pushremote_for_branch(branch, NULL));
+	if (!remote)
+		return error_buf(err,
+				 _("branch '%s' has no remote for pushing"),
+				 branch->name);
+
+	if (remote->push_refspec_nr) {
+		char *dst;
+		const char *ret;
+
+		dst = apply_refspecs(remote->push, remote->push_refspec_nr,
+				     branch->refname);
+		if (!dst)
+			return error_buf(err,
+					 _("push refspecs for '%s' do not include '%s'"),
+					 remote->name, branch->name);
+
+		ret = tracking_for_push_dest(remote, dst, err);
+		free(dst);
+		return ret;
+	}
+
+	if (remote->mirror)
+		return tracking_for_push_dest(remote, branch->refname, err);
+
+	switch (push_default) {
+	case PUSH_DEFAULT_NOTHING:
+		return error_buf(err, _("push has no destination (push.default is 'nothing')"));
+
+	case PUSH_DEFAULT_MATCHING:
+	case PUSH_DEFAULT_CURRENT:
+		return tracking_for_push_dest(remote, branch->refname, err);
+
+	case PUSH_DEFAULT_UPSTREAM:
+		return branch_get_upstream(branch, err);
+
+	case PUSH_DEFAULT_UNSPECIFIED:
+	case PUSH_DEFAULT_SIMPLE:
+		{
+			const char *up, *cur;
+
+			up = branch_get_upstream(branch, err);
+			if (!up)
+				return NULL;
+			cur = tracking_for_push_dest(remote, branch->refname, err);
+			if (!cur)
+				return NULL;
+			if (strcmp(cur, up))
+				return error_buf(err,
+						 _("cannot resolve 'simple' push to a single destination"));
+			return cur;
+		}
+	}
+
+	die("BUG: unhandled push situation");
+}
+
+const char *branch_get_push(struct branch *branch, struct strbuf *err)
+{
+	if (!branch->push_tracking_ref)
+		branch->push_tracking_ref = branch_get_push_1(branch, err);
+	return branch->push_tracking_ref;
+}
+
 static int ignore_symref_update(const char *refname)
 {
 	unsigned char sha1[20];
@@ -1877,12 +2023,15 @@
 
 /*
  * Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs.
+ * of commits) in *num_ours and *num_theirs. The name of the upstream branch
+ * (or NULL if no upstream is defined) is returned via *upstream_name, if it
+ * is not itself NULL.
  *
- * Return 0 if branch has no upstream (no base), -1 if upstream is missing
- * (with "gone" base), otherwise 1 (with base).
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist), 0 otherwise.
  */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+		       const char **upstream_name)
 {
 	unsigned char sha1[20];
 	struct commit *ours, *theirs;
@@ -1892,12 +2041,13 @@
 	int rev_argc;
 
 	/* 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;
+	base = branch_get_upstream(branch, NULL);
+	if (upstream_name)
+		*upstream_name = base;
+	if (!base)
+		return -1;
 
 	/* Cannot stat if what we used to build on no longer exists */
-	base = branch->merge[0]->dst;
 	if (read_ref(base, sha1))
 		return -1;
 	theirs = lookup_commit_reference(sha1);
@@ -1913,7 +2063,7 @@
 	/* are we the same? */
 	if (theirs == ours) {
 		*num_theirs = *num_ours = 0;
-		return 1;
+		return 0;
 	}
 
 	/* Run "rev-list --left-right ours...theirs" internally... */
@@ -1949,7 +2099,7 @@
 	/* clear object flags smudged by the above traversal */
 	clear_commit_marks(ours, ALL_REV_FLAGS);
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
-	return 1;
+	return 0;
 }
 
 /*
@@ -1958,23 +2108,17 @@
 int format_tracking_info(struct branch *branch, struct strbuf *sb)
 {
 	int ours, theirs;
+	const char *full_base;
 	char *base;
 	int upstream_is_gone = 0;
 
-	switch (stat_tracking_info(branch, &ours, &theirs)) {
-	case 0:
-		/* no base */
-		return 0;
-	case -1:
-		/* with "gone" base */
+	if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
+		if (!full_base)
+			return 0;
 		upstream_is_gone = 1;
-		break;
-	default:
-		/* with base */
-		break;
 	}
 
-	base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+	base = shorten_unambiguous_ref(full_base, 0);
 	if (upstream_is_gone) {
 		strbuf_addf(sb,
 			_("Your branch is based on '%s', but the upstream is gone.\n"),
@@ -2024,7 +2168,8 @@
 	return 1;
 }
 
-static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int one_local_ref(const char *refname, const struct object_id *oid,
+			 int flag, void *cb_data)
 {
 	struct ref ***local_tail = cb_data;
 	struct ref *ref;
@@ -2036,7 +2181,7 @@
 
 	len = strlen(refname) + 1;
 	ref = xcalloc(1, sizeof(*ref) + len);
-	hashcpy(ref->new_sha1, sha1);
+	hashcpy(ref->new_sha1, oid->hash);
 	memcpy(ref->name, refname, len);
 	**local_tail = ref;
 	*local_tail = &ref->next;
@@ -2046,6 +2191,7 @@
 struct ref *get_local_heads(void)
 {
 	struct ref *local_refs = NULL, **local_tail = &local_refs;
+
 	for_each_ref(one_local_ref, &local_tail);
 	return local_refs;
 }
@@ -2098,8 +2244,8 @@
 	int ref_count;
 };
 
-static int get_stale_heads_cb(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
+static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
+			      int flags, void *cb_data)
 {
 	struct stale_heads_info *info = cb_data;
 	struct string_list matches = STRING_LIST_INIT_DUP;
@@ -2128,7 +2274,7 @@
 
 	if (stale) {
 		struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
-		hashcpy(ref->new_sha1, sha1);
+		hashcpy(ref->new_sha1, oid->hash);
 	}
 
 clean_exit:
@@ -2141,6 +2287,7 @@
 	struct ref *ref, *stale_refs = NULL;
 	struct string_list ref_names = STRING_LIST_INIT_NODUP;
 	struct stale_heads_info info;
+
 	info.ref_names = &ref_names;
 	info.stale_refs_tail = &stale_refs;
 	info.refs = refs;
diff --git a/remote.h b/remote.h
index 02d66ce..312b7ca 100644
--- a/remote.h
+++ b/remote.h
@@ -203,19 +203,42 @@
 	const char *refname;
 
 	const char *remote_name;
-	struct remote *remote;
+	const char *pushremote_name;
 
 	const char **merge_name;
 	struct refspec **merge;
 	int merge_nr;
 	int merge_alloc;
+
+	const char *push_tracking_ref;
 };
 
 struct branch *branch_get(const char *name);
+const char *remote_for_branch(struct branch *branch, int *explicit);
+const char *pushremote_for_branch(struct branch *branch, int *explicit);
 
 int branch_has_merge_config(struct branch *branch);
 int branch_merge_matches(struct branch *, int n, const char *);
 
+/**
+ * Return the fully-qualified refname of the tracking branch for `branch`.
+ * I.e., what "branch@{upstream}" would give you. Returns NULL if no
+ * upstream is defined.
+ *
+ * If `err` is not NULL and no upstream is defined, a more specific error
+ * message is recorded there (if the function does not return NULL, then
+ * `err` is not touched).
+ */
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
+
+/**
+ * Return the tracking branch that corresponds to the ref we would push to
+ * given a bare `git push` while `branch` is checked out.
+ *
+ * The return value and `err` conventions match those of `branch_get_upstream`.
+ */
+const char *branch_get_push(struct branch *branch, struct strbuf *err);
+
 /* Flags to match_refs. */
 enum match_refs_flags {
 	MATCH_REFS_NONE		= 0,
@@ -226,7 +249,8 @@
 };
 
 /* Reporting of tracking info */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+		       const char **upstream_name);
 int format_tracking_info(struct branch *branch, struct strbuf *sb);
 
 struct ref *get_local_heads(void);
diff --git a/replace_object.c b/replace_object.c
index 0ab2dc1..f0b39f0 100644
--- a/replace_object.c
+++ b/replace_object.c
@@ -53,7 +53,7 @@
 }
 
 static int register_replace_ref(const char *refname,
-				const unsigned char *sha1,
+				const struct object_id *oid,
 				int flag, void *cb_data)
 {
 	/* Get sha1 from refname */
@@ -68,7 +68,7 @@
 	}
 
 	/* Copy sha1 from the read ref */
-	hashcpy(repl_obj->replacement, sha1);
+	hashcpy(repl_obj->replacement, oid->hash);
 
 	/* Register new object */
 	if (register_replace_object(repl_obj, 1))
diff --git a/revision.c b/revision.c
index 0b322b4..3ff8723 100644
--- a/revision.c
+++ b/revision.c
@@ -1218,7 +1218,8 @@
 	return 0;
 }
 
-static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_ref(const char *path, const struct object_id *oid,
+			  int flag, void *cb_data)
 {
 	struct all_refs_cb *cb = cb_data;
 	struct object *object;
@@ -1226,9 +1227,9 @@
 	if (ref_excluded(cb->all_revs->ref_excludes, path))
 	    return 0;
 
-	object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
+	object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags);
 	add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
-	add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
+	add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags);
 	return 0;
 }
 
@@ -1292,7 +1293,8 @@
 	return 0;
 }
 
-static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_reflog(const char *path, const struct object_id *oid,
+			     int flag, void *cb_data)
 {
 	struct all_refs_cb *cb = cb_data;
 	cb->warned_bad_reflog = 0;
@@ -1304,6 +1306,7 @@
 void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 {
 	struct all_refs_cb cb;
+
 	cb.all_revs = revs;
 	cb.all_flags = flags;
 	for_each_reflog(handle_one_reflog, &cb);
diff --git a/run-command.c b/run-command.c
index aad03ab..4d73e90 100644
--- a/run-command.c
+++ b/run-command.c
@@ -795,9 +795,9 @@
 #endif
 }
 
-char *find_hook(const char *name)
+const char *find_hook(const char *name)
 {
-	char *path = git_path("hooks/%s", name);
+	const char *path = git_path("hooks/%s", name);
 	if (access(path, X_OK) < 0)
 		path = NULL;
 
diff --git a/run-command.h b/run-command.h
index 263b966..1103805 100644
--- a/run-command.h
+++ b/run-command.h
@@ -52,7 +52,7 @@
 int finish_command(struct child_process *);
 int run_command(struct child_process *);
 
-extern char *find_hook(const char *name);
+extern const char *find_hook(const char *name);
 LAST_ARG_MUST_BE_NULL
 extern int run_hook_le(const char *const *env, const char *name, ...);
 extern int run_hook_ve(const char *const *env, const char *name, va_list args);
diff --git a/send-pack.c b/send-pack.c
index 2e07ac3..2a64fec 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -182,7 +182,7 @@
 {
 	struct strbuf *sb = cb;
 	if (graft->nr_parent == -1)
-		packet_buf_write(sb, "shallow %s\n", sha1_to_hex(graft->sha1));
+		packet_buf_write(sb, "shallow %s\n", oid_to_hex(&graft->oid));
 	return 0;
 }
 
diff --git a/server-info.c b/server-info.c
index 34b0253..c82e9ee 100644
--- a/server-info.c
+++ b/server-info.c
@@ -47,14 +47,15 @@
 	return ret;
 }
 
-static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int add_info_ref(const char *path, const struct object_id *oid,
+			int flag, void *cb_data)
 {
 	FILE *fp = cb_data;
-	struct object *o = parse_object(sha1);
+	struct object *o = parse_object(oid->hash);
 	if (!o)
 		return -1;
 
-	if (fprintf(fp, "%s	%s\n", sha1_to_hex(sha1), path) < 0)
+	if (fprintf(fp, "%s	%s\n", oid_to_hex(oid), path) < 0)
 		return -1;
 
 	if (o->type == OBJ_TAG) {
diff --git a/setup.c b/setup.c
index 9c32aae..82c0cc2 100644
--- a/setup.c
+++ b/setup.c
@@ -141,7 +141,9 @@
 		if (arg[2] == '\0') /* ":/" is root dir, always exists */
 			return 1;
 		name = arg + 2;
-	} else if (prefix)
+	} else if (!no_wildcard(arg))
+		return 1;
+	else if (prefix)
 		name = prefix_filename(prefix, strlen(prefix), arg);
 	else
 		name = arg;
@@ -225,6 +227,36 @@
 	    "'git <command> [<revision>...] -- [<file>...]'", arg);
 }
 
+int get_common_dir(struct strbuf *sb, const char *gitdir)
+{
+	struct strbuf data = STRBUF_INIT;
+	struct strbuf path = STRBUF_INIT;
+	const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
+	int ret = 0;
+	if (git_common_dir) {
+		strbuf_addstr(sb, git_common_dir);
+		return 1;
+	}
+	strbuf_addf(&path, "%s/commondir", gitdir);
+	if (file_exists(path.buf)) {
+		if (strbuf_read_file(&data, path.buf, 0) <= 0)
+			die_errno(_("failed to read %s"), path.buf);
+		while (data.len && (data.buf[data.len - 1] == '\n' ||
+				    data.buf[data.len - 1] == '\r'))
+			data.len--;
+		data.buf[data.len] = '\0';
+		strbuf_reset(&path);
+		if (!is_absolute_path(data.buf))
+			strbuf_addf(&path, "%s/", gitdir);
+		strbuf_addbuf(&path, &data);
+		strbuf_addstr(sb, real_path(path.buf));
+		ret = 1;
+	} else
+		strbuf_addstr(sb, gitdir);
+	strbuf_release(&data);
+	strbuf_release(&path);
+	return ret;
+}
 
 /*
  * Test if it looks like we're at a git directory.
@@ -239,31 +271,40 @@
  */
 int is_git_directory(const char *suspect)
 {
-	char path[PATH_MAX];
-	size_t len = strlen(suspect);
+	struct strbuf path = STRBUF_INIT;
+	int ret = 0;
+	size_t len;
 
-	if (PATH_MAX <= len + strlen("/objects"))
-		die("Too long path: %.*s", 60, suspect);
-	strcpy(path, suspect);
+	/* Check worktree-related signatures */
+	strbuf_addf(&path, "%s/HEAD", suspect);
+	if (validate_headref(path.buf))
+		goto done;
+
+	strbuf_reset(&path);
+	get_common_dir(&path, suspect);
+	len = path.len;
+
+	/* Check non-worktree-related signatures */
 	if (getenv(DB_ENVIRONMENT)) {
 		if (access(getenv(DB_ENVIRONMENT), X_OK))
-			return 0;
+			goto done;
 	}
 	else {
-		strcpy(path + len, "/objects");
-		if (access(path, X_OK))
-			return 0;
+		strbuf_setlen(&path, len);
+		strbuf_addstr(&path, "/objects");
+		if (access(path.buf, X_OK))
+			goto done;
 	}
 
-	strcpy(path + len, "/refs");
-	if (access(path, X_OK))
-		return 0;
+	strbuf_setlen(&path, len);
+	strbuf_addstr(&path, "/refs");
+	if (access(path.buf, X_OK))
+		goto done;
 
-	strcpy(path + len, "/HEAD");
-	if (validate_headref(path))
-		return 0;
-
-	return 1;
+	ret = 1;
+done:
+	strbuf_release(&path);
+	return ret;
 }
 
 int is_inside_git_dir(void)
@@ -309,9 +350,28 @@
 	initialized = 1;
 }
 
+static int check_repo_format(const char *var, const char *value, void *cb)
+{
+	if (strcmp(var, "core.repositoryformatversion") == 0)
+		repository_format_version = git_config_int(var, value);
+	else if (strcmp(var, "core.sharedrepository") == 0)
+		shared_repository = git_config_perm(var, value);
+	return 0;
+}
+
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 {
-	char repo_config[PATH_MAX+1];
+	struct strbuf sb = STRBUF_INIT;
+	const char *repo_config;
+	config_fn_t fn;
+	int ret = 0;
+
+	if (get_common_dir(&sb, gitdir))
+		fn = check_repo_format;
+	else
+		fn = check_repository_format_version;
+	strbuf_addstr(&sb, "/config");
+	repo_config = sb.buf;
 
 	/*
 	 * git_config() can't be used here because it calls git_pathdup()
@@ -322,8 +382,7 @@
 	 * Use a gentler version of git_config() to check if this repo
 	 * is a good one.
 	 */
-	snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
-	git_config_early(check_repository_format_version, NULL, repo_config);
+	git_config_early(fn, NULL, repo_config);
 	if (GIT_REPO_VERSION < repository_format_version) {
 		if (!nongit_ok)
 			die ("Expected git repo version <= %d, found %d",
@@ -332,9 +391,21 @@
 			GIT_REPO_VERSION, repository_format_version);
 		warning("Please upgrade Git");
 		*nongit_ok = -1;
-		return -1;
+		ret = -1;
 	}
-	return 0;
+	strbuf_release(&sb);
+	return ret;
+}
+
+static void update_linked_gitdir(const char *gitfile, const char *gitdir)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct stat st;
+
+	strbuf_addf(&path, "%s/gitfile", gitdir);
+	if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
+		write_file(path.buf, 0, "%s\n", gitfile);
+	strbuf_release(&path);
 }
 
 /*
@@ -385,6 +456,8 @@
 
 	if (!is_git_directory(dir))
 		die("Not a git repository: %s", dir);
+
+	update_linked_gitdir(path, dir);
 	path = real_path(dir);
 
 	free(buf);
@@ -807,11 +880,10 @@
 
 int check_repository_format_version(const char *var, const char *value, void *cb)
 {
-	if (strcmp(var, "core.repositoryformatversion") == 0)
-		repository_format_version = git_config_int(var, value);
-	else if (strcmp(var, "core.sharedrepository") == 0)
-		shared_repository = git_config_perm(var, value);
-	else if (strcmp(var, "core.bare") == 0) {
+	int ret = check_repo_format(var, value, cb);
+	if (ret)
+		return ret;
+	if (strcmp(var, "core.bare") == 0) {
 		is_bare_repository_cfg = git_config_bool(var, value);
 		if (is_bare_repository_cfg == 1)
 			inside_work_tree = -1;
diff --git a/sha1_file.c b/sha1_file.c
index 56c69ce..1cee438 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -405,7 +405,7 @@
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
-	char *alt = mkpath("%s\n", reference);
+	const char *alt = mkpath("%s\n", reference);
 	write_or_die(fd, alt, strlen(alt));
 	if (commit_lock_file(lock))
 		die("could not close alternates file");
@@ -443,6 +443,7 @@
 	read_info_alternates(get_object_directory(), 0);
 }
 
+/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
 static int freshen_file(const char *fn)
 {
 	struct utimbuf t;
@@ -450,11 +451,18 @@
 	return !utime(fn, &t);
 }
 
+/*
+ * All of the check_and_freshen functions return 1 if the file exists and was
+ * freshened (if freshening was requested), 0 otherwise. If they return
+ * 0, you should not assume that it is safe to skip a write of the object (it
+ * either does not exist on disk, or has a stale mtime and may be subject to
+ * pruning).
+ */
 static int check_and_freshen_file(const char *fn, int freshen)
 {
 	if (access(fn, F_OK))
 		return 0;
-	if (freshen && freshen_file(fn))
+	if (freshen && !freshen_file(fn))
 		return 0;
 	return 1;
 }
@@ -1571,6 +1579,40 @@
 	return git_inflate(stream, 0);
 }
 
+static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
+					unsigned long mapsize, void *buffer,
+					unsigned long bufsiz, struct strbuf *header)
+{
+	int status;
+
+	status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
+
+	/*
+	 * Check if entire header is unpacked in the first iteration.
+	 */
+	if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
+		return 0;
+
+	/*
+	 * buffer[0..bufsiz] was not large enough.  Copy the partial
+	 * result out to header, and then append the result of further
+	 * reading the stream.
+	 */
+	strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
+	stream->next_out = buffer;
+	stream->avail_out = bufsiz;
+
+	do {
+		status = git_inflate(stream, 0);
+		strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
+		if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
+			return 0;
+		stream->next_out = buffer;
+		stream->avail_out = bufsiz;
+	} while (status != Z_STREAM_END);
+	return -1;
+}
+
 static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
 {
 	int bytes = strlen(buffer) + 1;
@@ -1621,27 +1663,38 @@
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-int parse_sha1_header(const char *hdr, unsigned long *sizep)
+static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
+			       unsigned int flags)
 {
-	char type[10];
-	int i;
+	const char *type_buf = hdr;
 	unsigned long size;
+	int type, type_len = 0;
 
 	/*
-	 * The type can be at most ten bytes (including the
-	 * terminating '\0' that we add), and is followed by
+	 * The type can be of any size but is followed by
 	 * a space.
 	 */
-	i = 0;
 	for (;;) {
 		char c = *hdr++;
 		if (c == ' ')
 			break;
-		type[i++] = c;
-		if (i >= sizeof(type))
-			return -1;
+		type_len++;
 	}
-	type[i] = 0;
+
+	type = type_from_string_gently(type_buf, type_len, 1);
+	if (oi->typename)
+		strbuf_add(oi->typename, type_buf, type_len);
+	/*
+	 * Set type to 0 if its an unknown object and
+	 * we're obtaining the type using '--allow-unkown-type'
+	 * option.
+	 */
+	if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
+		type = 0;
+	else if (type < 0)
+		die("invalid object type");
+	if (oi->typep)
+		*oi->typep = type;
 
 	/*
 	 * The length must follow immediately, and be in canonical
@@ -1659,12 +1712,24 @@
 			size = size * 10 + c;
 		}
 	}
-	*sizep = size;
+
+	if (oi->sizep)
+		*oi->sizep = size;
 
 	/*
 	 * The length must be followed by a zero byte
 	 */
-	return *hdr ? -1 : type_from_string(type);
+	return *hdr ? -1 : type;
+}
+
+int parse_sha1_header(const char *hdr, unsigned long *sizep)
+{
+	struct object_info oi;
+
+	oi.sizep = sizep;
+	oi.typename = NULL;
+	oi.typep = NULL;
+	return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
 }
 
 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@ -2529,13 +2594,15 @@
 }
 
 static int sha1_loose_object_info(const unsigned char *sha1,
-				  struct object_info *oi)
+				  struct object_info *oi,
+				  int flags)
 {
-	int status;
-	unsigned long mapsize, size;
+	int status = 0;
+	unsigned long mapsize;
 	void *map;
 	git_zstream stream;
 	char hdr[32];
+	struct strbuf hdrbuf = STRBUF_INIT;
 
 	if (oi->delta_base_sha1)
 		hashclr(oi->delta_base_sha1);
@@ -2548,7 +2615,7 @@
 	 * return value implicitly indicates whether the
 	 * object even exists.
 	 */
-	if (!oi->typep && !oi->sizep) {
+	if (!oi->typep && !oi->typename && !oi->sizep) {
 		struct stat st;
 		if (stat_sha1_file(sha1, &st) < 0)
 			return -1;
@@ -2562,17 +2629,26 @@
 		return -1;
 	if (oi->disk_sizep)
 		*oi->disk_sizep = mapsize;
-	if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
+	if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
+		if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
+			status = error("unable to unpack %s header with --allow-unknown-type",
+				       sha1_to_hex(sha1));
+	} else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 		status = error("unable to unpack %s header",
 			       sha1_to_hex(sha1));
-	else if ((status = parse_sha1_header(hdr, &size)) < 0)
+	if (status < 0)
+		; /* Do nothing */
+	else if (hdrbuf.len) {
+		if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
+			status = error("unable to parse %s header with --allow-unknown-type",
+				       sha1_to_hex(sha1));
+	} else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
 		status = error("unable to parse %s header", sha1_to_hex(sha1));
-	else if (oi->sizep)
-		*oi->sizep = size;
 	git_inflate_end(&stream);
 	munmap(map, mapsize);
-	if (oi->typep)
+	if (status && oi->typep)
 		*oi->typep = status;
+	strbuf_release(&hdrbuf);
 	return 0;
 }
 
@@ -2581,6 +2657,7 @@
 	struct cached_object *co;
 	struct pack_entry e;
 	int rtype;
+	enum object_type real_type;
 	const unsigned char *real = lookup_replace_object_extended(sha1, flags);
 
 	co = find_cached_object(real);
@@ -2593,13 +2670,15 @@
 			*(oi->disk_sizep) = 0;
 		if (oi->delta_base_sha1)
 			hashclr(oi->delta_base_sha1);
+		if (oi->typename)
+			strbuf_addstr(oi->typename, typename(co->type));
 		oi->whence = OI_CACHED;
 		return 0;
 	}
 
 	if (!find_pack_entry(real, &e)) {
 		/* Most likely it's a loose object. */
-		if (!sha1_loose_object_info(real, oi)) {
+		if (!sha1_loose_object_info(real, oi, flags)) {
 			oi->whence = OI_LOOSE;
 			return 0;
 		}
@@ -2610,9 +2689,18 @@
 			return -1;
 	}
 
+	/*
+	 * packed_object_info() does not follow the delta chain to
+	 * find out the real type, unless it is given oi->typep.
+	 */
+	if (oi->typename && !oi->typep)
+		oi->typep = &real_type;
+
 	rtype = packed_object_info(e.p, e.offset, oi);
 	if (rtype < 0) {
 		mark_bad_packed_object(e.p, real);
+		if (oi->typep == &real_type)
+			oi->typep = NULL;
 		return sha1_object_info_extended(real, oi, 0);
 	} else if (in_delta_base_cache(e.p, e.offset)) {
 		oi->whence = OI_DBCACHED;
@@ -2623,6 +2711,10 @@
 		oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
 					 rtype == OBJ_OFS_DELTA);
 	}
+	if (oi->typename)
+		strbuf_addstr(oi->typename, typename(*oi->typep));
+	if (oi->typep == &real_type)
+		oi->typep = NULL;
 
 	return 0;
 }
@@ -3084,7 +3176,7 @@
 	return find_pack_entry(sha1, &e);
 }
 
-int has_sha1_file(const unsigned char *sha1)
+int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
 {
 	struct pack_entry e;
 
@@ -3092,6 +3184,8 @@
 		return 1;
 	if (has_loose_object(sha1))
 		return 1;
+	if (flags & HAS_SHA1_QUICK)
+		return 0;
 	reprepare_packed_git();
 	return find_pack_entry(sha1, &e);
 }
@@ -3489,14 +3583,19 @@
 {
 	struct packed_git *p;
 	int r = 0;
+	int pack_errors = 0;
 
 	prepare_packed_git();
 	for (p = packed_git; p; p = p->next) {
 		if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
 			continue;
+		if (open_pack_index(p)) {
+			pack_errors = 1;
+			continue;
+		}
 		r = for_each_object_in_pack(p, cb, data);
 		if (r)
 			break;
 	}
-	return r;
+	return r ? r : pack_errors;
 }
diff --git a/sha1_name.c b/sha1_name.c
index 6de8c87..e57513e 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -416,12 +416,12 @@
 	return slash;
 }
 
-static inline int upstream_mark(const char *string, int len)
+static inline int at_mark(const char *string, int len,
+			  const char **suffix, int nr)
 {
-	const char *suffix[] = { "@{upstream}", "@{u}" };
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+	for (i = 0; i < nr; i++) {
 		int suffix_len = strlen(suffix[i]);
 		if (suffix_len <= len
 		    && !memcmp(string, suffix[i], suffix_len))
@@ -430,6 +430,18 @@
 	return 0;
 }
 
+static inline int upstream_mark(const char *string, int len)
+{
+	const char *suffix[] = { "@{upstream}", "@{u}" };
+	return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
+static inline int push_mark(const char *string, int len)
+{
+	const char *suffix[] = { "@{push}" };
+	return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
 static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
 static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
 
@@ -477,7 +489,8 @@
 					nth_prior = 1;
 					continue;
 				}
-				if (!upstream_mark(str + at, len - at)) {
+				if (!upstream_mark(str + at, len - at) &&
+				    !push_mark(str + at, len - at)) {
 					reflog_len = (len-1) - (at+2);
 					len = at;
 				}
@@ -832,11 +845,11 @@
 /* Remember to update object flag allocation in object.h */
 #define ONELINE_SEEN (1u<<20)
 
-static int handle_one_ref(const char *path,
-		const unsigned char *sha1, int flag, void *cb_data)
+static int handle_one_ref(const char *path, const struct object_id *oid,
+			  int flag, void *cb_data)
 {
 	struct commit_list **list = cb_data;
-	struct object *object = parse_object(sha1);
+	struct object *object = parse_object(oid->hash);
 	if (!object)
 		return 0;
 	if (object->type == OBJ_TAG) {
@@ -1056,46 +1069,36 @@
 	free(s);
 }
 
-static const char *get_upstream_branch(const char *branch_buf, int len)
-{
-	char *branch = xstrndup(branch_buf, len);
-	struct branch *upstream = branch_get(*branch ? branch : NULL);
-
-	/*
-	 * Upstream can be NULL only if branch refers to HEAD and HEAD
-	 * points to something different than a branch.
-	 */
-	if (!upstream)
-		die(_("HEAD does not point to a branch"));
-	if (!upstream->merge || !upstream->merge[0]->dst) {
-		if (!ref_exists(upstream->refname))
-			die(_("No such branch: '%s'"), branch);
-		if (!upstream->merge) {
-			die(_("No upstream configured for branch '%s'"),
-				upstream->name);
-		}
-		die(
-			_("Upstream branch '%s' not stored as a remote-tracking branch"),
-			upstream->merge[0]->src);
-	}
-	free(branch);
-
-	return upstream->merge[0]->dst;
-}
-
-static int interpret_upstream_mark(const char *name, int namelen,
-				   int at, struct strbuf *buf)
+static int interpret_branch_mark(const char *name, int namelen,
+				 int at, struct strbuf *buf,
+				 int (*get_mark)(const char *, int),
+				 const char *(*get_data)(struct branch *,
+							 struct strbuf *))
 {
 	int len;
+	struct branch *branch;
+	struct strbuf err = STRBUF_INIT;
+	const char *value;
 
-	len = upstream_mark(name + at, namelen - at);
+	len = get_mark(name + at, namelen - at);
 	if (!len)
 		return -1;
 
 	if (memchr(name, ':', at))
 		return -1;
 
-	set_shortened_ref(buf, get_upstream_branch(name, at));
+	if (at) {
+		char *name_str = xmemdupz(name, at);
+		branch = branch_get(name_str);
+		free(name_str);
+	} else
+		branch = branch_get(NULL);
+
+	value = get_data(branch, &err);
+	if (!value)
+		die("%s", err.buf);
+
+	set_shortened_ref(buf, value);
 	return len + at;
 }
 
@@ -1146,7 +1149,13 @@
 		if (len > 0)
 			return reinterpret(name, namelen, len, buf);
 
-		len = interpret_upstream_mark(name, namelen, at - name, buf);
+		len = interpret_branch_mark(name, namelen, at - name, buf,
+					    upstream_mark, branch_get_upstream);
+		if (len > 0)
+			return len;
+
+		len = interpret_branch_mark(name, namelen, at - name, buf,
+					    push_mark, branch_get_push);
 		if (len > 0)
 			return len;
 	}
@@ -1370,6 +1379,7 @@
 		int pos;
 		if (!only_to_die && namelen > 2 && name[1] == '/') {
 			struct commit_list *list = NULL;
+
 			for_each_ref(handle_one_ref, &list);
 			commit_list_sort_by_date(&list);
 			return get_sha1_oneline(name + 2, sha1, list);
@@ -1433,11 +1443,19 @@
 			new_filename = resolve_relative_path(filename);
 			if (new_filename)
 				filename = new_filename;
-			ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
-			if (ret && only_to_die) {
-				diagnose_invalid_sha1_path(prefix, filename,
-							   tree_sha1,
-							   name, len);
+			if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
+				ret = get_tree_entry_follow_symlinks(tree_sha1,
+					filename, sha1, &oc->symlink_path,
+					&oc->mode);
+			} else {
+				ret = get_tree_entry(tree_sha1, filename,
+						     sha1, &oc->mode);
+				if (ret && only_to_die) {
+					diagnose_invalid_sha1_path(prefix,
+								   filename,
+								   tree_sha1,
+								   name, len);
+				}
 			}
 			hashcpy(oc->tree, tree_sha1);
 			strlcpy(oc->path, filename, sizeof(oc->path));
@@ -1468,5 +1486,7 @@
 
 int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
 {
+	if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
+		die("BUG: incompatible flags for get_sha1_with_context");
 	return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
 }
diff --git a/shallow.c b/shallow.c
index d8bf40a..257d811 100644
--- a/shallow.c
+++ b/shallow.c
@@ -31,7 +31,7 @@
 		xmalloc(sizeof(struct commit_graft));
 	struct commit *commit = lookup_commit(sha1);
 
-	hashcpy(graft->sha1, sha1);
+	hashcpy(graft->oid.hash, sha1);
 	graft->nr_parent = -1;
 	if (commit && commit->object.parsed)
 		commit->parents = NULL;
@@ -159,11 +159,11 @@
 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);
+	const char *hex = oid_to_hex(&graft->oid);
 	if (graft->nr_parent != -1)
 		return 0;
 	if (data->flags & SEEN_ONLY) {
-		struct commit *c = lookup_commit(graft->sha1);
+		struct commit *c = lookup_commit(graft->oid.hash);
 		if (!c || !(c->object.flags & SEEN)) {
 			if (data->flags & VERBOSE)
 				printf("Removing %s from .git/shallow\n",
@@ -282,7 +282,7 @@
 {
 	int fd = *(int *)cb;
 	if (graft->nr_parent == -1)
-		packet_write(fd, "shallow %s\n", sha1_to_hex(graft->sha1));
+		packet_write(fd, "shallow %s\n", oid_to_hex(&graft->oid));
 	return 0;
 }
 
@@ -475,11 +475,10 @@
 	free(tmp);
 }
 
-static int mark_uninteresting(const char *refname,
-			      const unsigned char *sha1,
+static int mark_uninteresting(const char *refname, const struct object_id *oid,
 			      int flags, void *cb_data)
 {
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
 	if (!commit)
 		return 0;
 	commit->object.flags |= UNINTERESTING;
@@ -584,12 +583,12 @@
 	int nr, alloc;
 };
 
-static int add_ref(const char *refname,
-		   const unsigned char *sha1, int flags, void *cb_data)
+static int add_ref(const char *refname, const struct object_id *oid,
+		   int flags, void *cb_data)
 {
 	struct commit_array *ca = cb_data;
 	ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
-	ca->commits[ca->nr] = lookup_commit_reference_gently(sha1, 1);
+	ca->commits[ca->nr] = lookup_commit_reference_gently(oid->hash, 1);
 	if (ca->commits[ca->nr])
 		ca->nr++;
 	return 0;
@@ -674,6 +673,7 @@
 
 		if (!si->commits) {
 			struct commit_array ca;
+
 			memset(&ca, 0, sizeof(ca));
 			head_ref(add_ref, &ca);
 			for_each_ref(add_ref, &ca);
diff --git a/split-index.c b/split-index.c
index 21485e2..968b780 100644
--- a/split-index.c
+++ b/split-index.c
@@ -41,13 +41,6 @@
 	return 0;
 }
 
-static int write_strbuf(void *user_data, const void *data, size_t len)
-{
-	struct strbuf *sb = user_data;
-	strbuf_add(sb, data, len);
-	return len;
-}
-
 int write_link_extension(struct strbuf *sb,
 			 struct index_state *istate)
 {
@@ -55,8 +48,8 @@
 	strbuf_add(sb, si->base_sha1, 20);
 	if (!si->delete_bitmap && !si->replace_bitmap)
 		return 0;
-	ewah_serialize_to(si->delete_bitmap, write_strbuf, sb);
-	ewah_serialize_to(si->replace_bitmap, write_strbuf, sb);
+	ewah_serialize_strbuf(si->delete_bitmap, sb);
+	ewah_serialize_strbuf(si->replace_bitmap, sb);
 	return 0;
 }
 
diff --git a/strbuf.c b/strbuf.c
index 88cafd4..bbaf32e 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -435,6 +435,47 @@
 	return -1;
 }
 
+#ifdef HAVE_GETDELIM
+int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
+{
+	ssize_t r;
+
+	if (feof(fp))
+		return EOF;
+
+	strbuf_reset(sb);
+
+	/* Translate slopbuf to NULL, as we cannot call realloc on it */
+	if (!sb->alloc)
+		sb->buf = NULL;
+	r = getdelim(&sb->buf, &sb->alloc, term, fp);
+
+	if (r > 0) {
+		sb->len = r;
+		return 0;
+	}
+	assert(r == -1);
+
+	/*
+	 * Normally we would have called xrealloc, which will try to free
+	 * memory and recover. But we have no way to tell getdelim() to do so.
+	 * Worse, we cannot try to recover ENOMEM ourselves, because we have
+	 * no idea how many bytes were read by getdelim.
+	 *
+	 * Dying here is reasonable. It mirrors what xrealloc would do on
+	 * catastrophic memory failure. We skip the opportunity to free pack
+	 * memory and retry, but that's unlikely to help for a malloc small
+	 * enough to hold a single line of input, anyway.
+	 */
+	if (errno == ENOMEM)
+		die("Out of memory, getdelim failed");
+
+	/* Restore slopbuf that we moved out of the way before */
+	if (!sb->buf)
+		strbuf_init(sb, 0);
+	return EOF;
+}
+#else
 int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
 {
 	int ch;
@@ -443,18 +484,22 @@
 		return EOF;
 
 	strbuf_reset(sb);
-	while ((ch = fgetc(fp)) != EOF) {
-		strbuf_grow(sb, 1);
+	flockfile(fp);
+	while ((ch = getc_unlocked(fp)) != EOF) {
+		if (!strbuf_avail(sb))
+			strbuf_grow(sb, 1);
 		sb->buf[sb->len++] = ch;
 		if (ch == term)
 			break;
 	}
+	funlockfile(fp);
 	if (ch == EOF && sb->len == 0)
 		return EOF;
 
 	sb->buf[sb->len] = '\0';
 	return 0;
 }
+#endif
 
 int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
 {
@@ -481,9 +526,10 @@
 	return 0;
 }
 
-int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
+ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 {
-	int fd, len;
+	int fd;
+	ssize_t len;
 
 	fd = open(path, O_RDONLY);
 	if (fd < 0)
diff --git a/strbuf.h b/strbuf.h
index 1883494..4a48c00 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -205,7 +205,8 @@
  */
 static inline void strbuf_addch(struct strbuf *sb, int c)
 {
-	strbuf_grow(sb, 1);
+	if (!strbuf_avail(sb))
+		strbuf_grow(sb, 1);
 	sb->buf[sb->len++] = c;
 	sb->buf[sb->len] = '\0';
 }
@@ -364,7 +365,7 @@
  * Read the contents of a file, specified by its path. The third argument
  * can be used to give a hint about the file size, to avoid reallocs.
  */
-extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 
 /**
  * Read the target of a symbolic link, specified by its path.  The third
diff --git a/submodule.c b/submodule.c
index 5a563ad..15e90d1 100644
--- a/submodule.c
+++ b/submodule.c
@@ -422,7 +422,8 @@
 	config_fetch_recurse_submodules = value;
 }
 
-static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+static int has_remote(const char *refname, const struct object_id *oid,
+		      int flags, void *cb_data)
 {
 	return 1;
 }
@@ -616,10 +617,10 @@
 	}
 }
 
-static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
+static int add_sha1_to_array(const char *ref, const struct object_id *oid,
 			     int flags, void *data)
 {
-	sha1_array_append(data, sha1);
+	sha1_array_append(data, oid->hash);
 	return 0;
 }
 
@@ -1099,16 +1100,11 @@
 	struct strbuf file_name = STRBUF_INIT;
 	struct strbuf rel_path = STRBUF_INIT;
 	const char *real_work_tree = xstrdup(real_path(work_tree));
-	FILE *fp;
 
 	/* Update gitfile */
 	strbuf_addf(&file_name, "%s/.git", work_tree);
-	fp = fopen(file_name.buf, "w");
-	if (!fp)
-		die(_("Could not create git link %s"), file_name.buf);
-	fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
-						  &rel_path));
-	fclose(fp);
+	write_file(file_name.buf, 1, "gitdir: %s\n",
+		   relative_path(git_dir, real_work_tree, &rel_path));
 
 	/* Update core.worktree setting */
 	strbuf_reset(&file_name);
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index 5aa8adc..7548225 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -69,7 +69,7 @@
 	(
 		cd "$db" &&
 		{
-			p4d -q -p $P4DPORT &
+			p4d -q -p $P4DPORT "$@" &
 			echo $! >"$pidfile"
 		}
 	) &&
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index f8ed857..de2a224 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -39,14 +39,14 @@
 
 	# now kill off all of the refs and pretend we had
 	# just the one tip
-	rm -rf .git/logs .git/refs/* .git/packed-refs
-	git update-ref HEAD $cutoff
+	rm -rf .git/logs .git/refs/* .git/packed-refs &&
+	git update-ref HEAD $cutoff &&
 
 	# and then repack, which will leave us with a nice
 	# big bitmap pack of the "old" history, and all of
 	# the new history will be loose, as if it had been pushed
 	# up incrementally and exploded via unpack-objects
-	git repack -Ad
+	git repack -Ad &&
 
 	# and now restore our original tip, as if the pushes
 	# had happened
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index e9f1626..718efa0 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -204,6 +204,16 @@
 	GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
 '
 
+test_expect_success 'filter that does not read is fine' '
+	test-genrandom foo $((128 * 1024 + 1)) >big &&
+	echo "big filter=epipe" >.gitattributes &&
+	git config filter.epipe.clean "echo xyzzy" &&
+	git add big &&
+	git cat-file blob :big >actual &&
+	echo xyzzy >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success EXPENSIVE 'filter large file' '
 	git config filter.largefile.smudge cat &&
 	git config filter.largefile.clean cat &&
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 452320d..1a56e5e 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -57,28 +57,35 @@
 
 check_warning () {
 	case "$1" in
-	LF_CRLF) grep "LF will be replaced by CRLF" $2;;
-	CRLF_LF) grep "CRLF will be replaced by LF" $2;;
-	'')
-		>expect
-		grep "will be replaced by" $2 >actual
-		test_cmp expect actual
-		;;
-	*) false ;;
+	LF_CRLF) echo "warning: LF will be replaced by CRLF" >"$2".expect ;;
+	CRLF_LF) echo "warning: CRLF will be replaced by LF" >"$2".expect ;;
+	'')	                                                 >"$2".expect ;;
+	*) echo >&2 "Illegal 1": "$1" ; return false ;;
 	esac
+	grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" >"$2".actual
+	test_cmp "$2".expect "$2".actual
 }
 
-create_file_in_repo () {
+commit_check_warn () {
 	crlf=$1
 	attr=$2
 	lfname=$3
 	crlfname=$4
-	lfmixcrlf=$5
-	lfmixcr=$6
-	crlfnul=$7
-	create_gitattributes "$attr" &&
+	repoMIX=$5
+	lfmixcrlf=$6
+	lfmixcr=$7
+	crlfnul=$8
 	pfx=crlf_${crlf}_attr_${attr}
-	for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+	# Special handling for repoMIX: It should already be in the repo
+	# with CRLF
+	f=repoMIX
+	fname=${pfx}_$f.txt
+	echo >.gitattributes &&
+	cp $f $fname &&
+	git -c core.autocrlf=false add $fname 2>"${pfx}_$f.err" &&
+	git commit -m "repoMIX" &&
+	create_gitattributes "$attr" &&
+	for f in LF CRLF repoMIX LF_mix_CR CRLF_mix_LF LF_nul CRLF_nul
 	do
 		fname=${pfx}_$f.txt &&
 		cp $f $fname &&
@@ -109,7 +116,7 @@
 }
 
 
-check_files_in_ws () {
+checkout_files () {
 	eol=$1
 	crlf=$2
 	attr=$3
@@ -122,7 +129,7 @@
 	git config core.autocrlf $crlf &&
 	pfx=eol_${eol}_crlf_${crlf}_attr_${attr}_ &&
 	src=crlf_false_attr__ &&
-	for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+	for f in LF CRLF LF_mix_CR CRLF_mix_LF LF_nul
 	do
 		rm $src$f.txt &&
 		if test -z "$eol"; then
@@ -144,8 +151,8 @@
 	test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF_mix_CR" "
 		compare_ws_file $pfx $lfmixcr   ${src}LF_mix_CR.txt
 	"
-	test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=CRLF_nul" "
-		compare_ws_file $pfx $crlfnul   ${src}CRLF_nul.txt
+	test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$attr file=LF_nul" "
+		compare_ws_file $pfx $crlfnul   ${src}LF_nul.txt
 	"
 }
 
@@ -157,6 +164,7 @@
 	git commit -m "add .gitattributes" "" &&
 	printf "line1\nline2\nline3"     >LF &&
 	printf "line1\r\nline2\r\nline3" >CRLF &&
+	printf "line1\r\nline2\nline3"   >repoMIX &&
 	printf "line1\r\nline2\nline3"   >CRLF_mix_LF &&
 	printf "line1\nline2\rline3"     >LF_mix_CR &&
 	printf "line1\r\nline2\rline3"   >CRLF_mix_CR &&
@@ -169,40 +177,55 @@
 warn_LF_CRLF="LF will be replaced by CRLF"
 warn_CRLF_LF="CRLF will be replaced by LF"
 
-test_expect_success 'add files empty attr' '
-	create_file_in_repo false ""     ""        ""        ""        ""        "" &&
-	create_file_in_repo true  ""     "LF_CRLF" ""        "LF_CRLF" ""        "" &&
-	create_file_in_repo input ""     ""        "CRLF_LF" "CRLF_LF" ""        ""
+# WILC stands for "Warn if (this OS) converts LF into CRLF".
+# WICL: Warn if CRLF becomes LF
+# WAMIX: Mixed line endings: either CRLF->LF or LF->CRLF
+if test_have_prereq NATIVE_CRLF
+then
+	WILC=LF_CRLF
+	WICL=
+	WAMIX=LF_CRLF
+else
+	WILC=
+	WICL=CRLF_LF
+	WAMIX=CRLF_LF
+fi
+
+#                         attr   LF        CRLF      repoMIX   CRLFmixLF LFmixCR   CRLFNUL
+test_expect_success 'commit files empty attr' '
+	commit_check_warn false ""     ""        ""        ""        ""        ""        "" &&
+	commit_check_warn true  ""     "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""        "" &&
+	commit_check_warn input ""     ""        "CRLF_LF" "CRLF_LF" "CRLF_LF" ""        ""
 '
 
-test_expect_success 'add files attr=auto' '
-	create_file_in_repo false "auto" ""        "CRLF_LF" "CRLF_LF" ""        "" &&
-	create_file_in_repo true  "auto" "LF_CRLF" ""        "LF_CRLF" ""        "" &&
-	create_file_in_repo input "auto" ""        "CRLF_LF" "CRLF_LF" ""        ""
+test_expect_success 'commit files attr=auto' '
+	commit_check_warn false "auto" "$WILC"   "$WICL"   "$WAMIX"  "$WAMIX"  ""        "" &&
+	commit_check_warn true  "auto" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""        "" &&
+	commit_check_warn input "auto" ""        "CRLF_LF" "CRLF_LF" "CRLF_LF" ""        ""
 '
 
-test_expect_success 'add files attr=text' '
-	create_file_in_repo false "text" ""        "CRLF_LF" "CRLF_LF" ""        "CRLF_LF" &&
-	create_file_in_repo true  "text" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""        &&
-	create_file_in_repo input "text" ""        "CRLF_LF" "CRLF_LF" ""        "CRLF_LF"
+test_expect_success 'commit files attr=text' '
+	commit_check_warn false "text" "$WILC"   "$WICL"   "$WAMIX"  "$WAMIX"  "$WILC"   "$WICL"   &&
+	commit_check_warn true  "text" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "LF_CRLF" ""        &&
+	commit_check_warn input "text" ""        "CRLF_LF" "CRLF_LF" "CRLF_LF" ""        "CRLF_LF"
 '
 
-test_expect_success 'add files attr=-text' '
-	create_file_in_repo false "-text" ""       ""        ""        ""        "" &&
-	create_file_in_repo true  "-text" ""       ""        ""        ""        "" &&
-	create_file_in_repo input "-text" ""       ""        ""        ""        ""
+test_expect_success 'commit files attr=-text' '
+	commit_check_warn false "-text" ""       ""        ""        ""        ""        "" &&
+	commit_check_warn true  "-text" ""       ""        ""        ""        ""        "" &&
+	commit_check_warn input "-text" ""       ""        ""        ""        ""        ""
 '
 
-test_expect_success 'add files attr=lf' '
-	create_file_in_repo false "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
-	create_file_in_repo true  "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
-	create_file_in_repo input "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF"
+test_expect_success 'commit files attr=lf' '
+	commit_check_warn false "lf"    ""       "CRLF_LF" "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
+	commit_check_warn true  "lf"    ""       "CRLF_LF" "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
+	commit_check_warn input "lf"    ""       "CRLF_LF" "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF"
 '
 
-test_expect_success 'add files attr=crlf' '
-	create_file_in_repo false "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "" &&
-	create_file_in_repo true  "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "" &&
-	create_file_in_repo input "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""
+test_expect_success 'commit files attr=crlf' '
+	commit_check_warn false "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "LF_CRLF" "" &&
+	commit_check_warn true  "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "LF_CRLF" "" &&
+	commit_check_warn input "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "LF_CRLF" ""
 '
 
 test_expect_success 'create files cleanup' '
@@ -237,7 +260,7 @@
 ################################################################################
 # Check how files in the repo are changed when they are checked out
 # How to read the table below:
-# - check_files_in_ws will check multiple files with a combination of settings
+# - checkout_files will check multiple files with a combination of settings
 #   and attributes (core.autocrlf=input is forbidden with core.eol=crlf)
 # - parameter $1 : core.eol               lf | crlf
 # - parameter $2 : core.autocrlf          false | true | input
@@ -249,87 +272,89 @@
 # - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
 
 #                                            What we have in the repo:
-#                														 ----------------- EOL in repo ----------------
-#                														 LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
+#                                            ----------------- EOL in repo ----------------
+#                                            LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
 #                   settings with checkout:
 #                   core.   core.   .gitattr
 #                    eol     acrlf
 #                                            ----------------------------------------------
 #                                            What we want to have in the working tree:
-if test_have_prereq MINGW
+if test_have_prereq NATIVE_CRLF
 then
 MIX_CRLF_LF=CRLF
 MIX_LF_CR=CRLF_mix_CR
 NL=CRLF
+LFNUL=CRLF_nul
 else
 MIX_CRLF_LF=CRLF_mix_LF
 MIX_LF_CR=LF_mix_CR
 NL=LF
+LFNUL=LF_nul
 fi
 export CRLF_MIX_LF_CR MIX NL
 
-check_files_in_ws    lf      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    lf      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      input "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    lf      false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    lf      true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    lf      input "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    lf      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      false "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+checkout_files    lf      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      false "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    lf      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      input "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    lf      false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    lf      true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    lf      input "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
 
-check_files_in_ws    crlf    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    false "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    false "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    crlf    true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    crlf    false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    crlf    false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    crlf    true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    crlf    false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    false "auto"    CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+checkout_files    crlf    true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+checkout_files    crlf    false "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    crlf    true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    crlf    false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    crlf    false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    crlf    true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
 
-check_files_in_ws    ""      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      false "auto"    $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      false "text"    $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   CRLF_nul
-check_files_in_ws    ""      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    ""      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      input "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    ""      false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    ""      true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    ""      input "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    ""      false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      input  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      false "auto"    $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
+checkout_files    ""      true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+checkout_files    ""      input "auto"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      false "text"    $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+checkout_files    ""      true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    ""      input "text"    LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      input "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      input "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    ""      false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    ""      true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    ""      input "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
 
-check_files_in_ws    native  false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  false "auto"    $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    CRLF_nul
-check_files_in_ws    native  true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    CRLF_nul
-check_files_in_ws    native  false "text"    $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   CRLF_nul
-check_files_in_ws    native  true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    native  false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    CRLF_nul
-check_files_in_ws    native  false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
-check_files_in_ws    native  true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    native  false  ""       LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  true   ""       CRLF  CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  false "auto"    $NL   CRLF  $MIX_CRLF_LF LF_mix_CR    LF_nul
+checkout_files    native  true  "auto"    CRLF  CRLF  CRLF         LF_mix_CR    LF_nul
+checkout_files    native  false "text"    $NL   CRLF  $MIX_CRLF_LF $MIX_LF_CR   $LFNUL
+checkout_files    native  true  "text"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    native  false "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  true  "-text"   LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  false "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  true  "lf"      LF    CRLF  CRLF_mix_LF  LF_mix_CR    LF_nul
+checkout_files    native  false "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
+checkout_files    native  true  "crlf"    CRLF  CRLF  CRLF         CRLF_mix_CR  CRLF_nul
 
 test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index c0143a0..93605f4 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -19,6 +19,14 @@
 	"test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
+test_git_path() {
+	test_expect_success "git-path $1 $2 => $3" "
+		$1 git rev-parse --git-path $2 >actual &&
+		echo $3 >expect &&
+		test_cmp expect actual
+	"
+}
+
 # On Windows, we are using MSYS's bash, which mangles the paths.
 # Absolute paths are anchored at the MSYS installation directory,
 # which means that the path / accounts for this many characters:
@@ -244,4 +252,32 @@
 relative_path "<null>"		"<null>"	./
 relative_path "<null>"		/foo/a/b	./
 
+test_git_path A=B                info/grafts .git/info/grafts
+test_git_path GIT_GRAFT_FILE=foo info/grafts foo
+test_git_path GIT_GRAFT_FILE=foo info/////grafts foo
+test_git_path GIT_INDEX_FILE=foo index foo
+test_git_path GIT_INDEX_FILE=foo index/foo .git/index/foo
+test_git_path GIT_INDEX_FILE=foo index2 .git/index2
+test_expect_success 'setup fake objects directory foo' 'mkdir foo'
+test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
+test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
+test_expect_success 'setup common repository' 'git --git-dir=bar init'
+test_git_path GIT_COMMON_DIR=bar index                    .git/index
+test_git_path GIT_COMMON_DIR=bar HEAD                     .git/HEAD
+test_git_path GIT_COMMON_DIR=bar logs/HEAD                .git/logs/HEAD
+test_git_path GIT_COMMON_DIR=bar objects                  bar/objects
+test_git_path GIT_COMMON_DIR=bar objects/bar              bar/objects/bar
+test_git_path GIT_COMMON_DIR=bar info/exclude             bar/info/exclude
+test_git_path GIT_COMMON_DIR=bar info/grafts              bar/info/grafts
+test_git_path GIT_COMMON_DIR=bar info/sparse-checkout     .git/info/sparse-checkout
+test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar
+test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar
+test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master   bar/logs/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar refs/heads/master        bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me
+test_git_path GIT_COMMON_DIR=bar config                   bar/config
+test_git_path GIT_COMMON_DIR=bar packed-refs              bar/packed-refs
+test_git_path GIT_COMMON_DIR=bar shallow                  bar/shallow
+
 test_done
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index 0979df9..1d8d1f2 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -75,7 +75,7 @@
 	EOF
 '
 
-test_expect_success POSIXPERM 'get: use xdg file if home file is unreadable' '
+test_expect_success POSIXPERM,SANITY 'get: use xdg file if home file is unreadable' '
 	echo "https://home-user:home-pass@example.com" >"$HOME/.git-credentials" &&
 	chmod -r "$HOME/.git-credentials" &&
 	mkdir -p "$HOME/.config/git" &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index ab36b1e..93a4794 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -47,6 +47,18 @@
 	test_cmp expect actual
     '
 
+    test_expect_success "Type of $type is correct using --allow-unknown-type" '
+	echo $type >expect &&
+	git cat-file -t --allow-unknown-type $sha1 >actual &&
+	test_cmp expect actual
+    '
+
+    test_expect_success "Size of $type is correct using --allow-unknown-type" '
+	echo $size >expect &&
+	git cat-file -s --allow-unknown-type $sha1 >actual &&
+	test_cmp expect actual
+    '
+
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
 	maybe_remove_timestamp "$content" $no_ts >expect &&
@@ -189,6 +201,13 @@
     '
 done
 
+for opt in t s e p
+do
+    test_expect_success "Passing -$opt with --follow-symlinks fails" '
+	    test_must_fail git cat-file --follow-symlinks -$opt $hello_sha1
+	'
+done
+
 test_expect_success "--batch-check for a non-existent named object" '
     test "foobar42 missing
 foobar84 missing" = \
@@ -296,4 +315,236 @@
 	}
 '
 
+bogus_type="bogus"
+bogus_content="bogus"
+bogus_size=$(strlen "$bogus_content")
+bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success "Type of broken object is correct" '
+	echo $bogus_type >expect &&
+	git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success "Size of broken object is correct" '
+	echo $bogus_size >expect &&
+	git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+	test_cmp expect actual
+'
+bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
+bogus_content="bogus"
+bogus_size=$(strlen "$bogus_content")
+bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
+
+test_expect_success "Type of broken object is correct when type is large" '
+	echo $bogus_type >expect &&
+	git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success "Size of large broken object is correct when type is large" '
+	echo $bogus_size >expect &&
+	git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
+	test_cmp expect actual
+'
+
+# Tests for git cat-file --follow-symlinks
+test_expect_success 'prep for symlink tests' '
+	echo_without_newline "$hello_content" >morx &&
+	test_ln_s_add morx same-dir-link &&
+	test_ln_s_add dir link-to-dir &&
+	test_ln_s_add ../fleem out-of-repo-link &&
+	test_ln_s_add .. out-of-repo-link-dir &&
+	test_ln_s_add same-dir-link link-to-link &&
+	test_ln_s_add nope broken-same-dir-link &&
+	mkdir dir &&
+	test_ln_s_add ../morx dir/parent-dir-link &&
+	test_ln_s_add .. dir/link-dir &&
+	test_ln_s_add ../../escape dir/out-of-repo-link &&
+	test_ln_s_add ../.. dir/out-of-repo-link-dir &&
+	test_ln_s_add nope dir/broken-link-in-dir &&
+	mkdir dir/subdir &&
+	test_ln_s_add ../../morx dir/subdir/grandparent-dir-link &&
+	test_ln_s_add ../../../great-escape dir/subdir/out-of-repo-link &&
+	test_ln_s_add ../../.. dir/subdir/out-of-repo-link-dir &&
+	test_ln_s_add ../../../ dir/subdir/out-of-repo-link-dir-trailing &&
+	test_ln_s_add ../parent-dir-link dir/subdir/parent-dir-link-to-link &&
+	echo_without_newline "$hello_content" >dir/subdir/ind2 &&
+	echo_without_newline "$hello_content" >dir/ind1 &&
+	test_ln_s_add dir dirlink &&
+	test_ln_s_add dir/subdir subdirlink &&
+	test_ln_s_add subdir/ind2 dir/link-to-child &&
+	test_ln_s_add dir/link-to-child link-to-down-link &&
+	test_ln_s_add dir/.. up-down &&
+	test_ln_s_add dir/../ up-down-trailing &&
+	test_ln_s_add dir/../morx up-down-file &&
+	test_ln_s_add dir/../../morx up-up-down-file &&
+	test_ln_s_add subdirlink/../../morx up-two-down-file &&
+	test_ln_s_add loop1 loop2 &&
+	test_ln_s_add loop2 loop1 &&
+	git add morx dir/subdir/ind2 dir/ind1 &&
+	git commit -am "test" &&
+	echo $hello_sha1 blob $hello_size >found
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for non-links' '
+	echo HEAD:morx | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo HEAD:nope missing >expect &&
+	echo HEAD:nope | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, same-dir links' '
+	echo HEAD:same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for in-repo, links to dirs' '
+	echo HEAD:link-to-dir/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for broken in-repo, same-dir links' '
+	echo dangling 25 >expect &&
+	echo HEAD:broken-same-dir-link >>expect &&
+	echo HEAD:broken-same-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for same-dir links-to-links' '
+	echo HEAD:link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for parent-dir links' '
+	echo HEAD:dir/parent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo notdir 29 >expect &&
+	echo HEAD:dir/parent-dir-link/nope >>expect &&
+	echo HEAD:dir/parent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for .. links' '
+	echo dangling 22 >expect &&
+	echo HEAD:dir/link-dir/nope >>expect &&
+	echo HEAD:dir/link-dir/nope | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:dir/link-dir/morx | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo dangling 27 >expect &&
+	echo HEAD:dir/broken-link-in-dir >>expect &&
+	echo HEAD:dir/broken-link-in-dir | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for ../.. links' '
+	echo notdir 41 >expect &&
+	echo HEAD:dir/subdir/grandparent-dir-link/nope >>expect &&
+	echo HEAD:dir/subdir/grandparent-dir-link/nope | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:dir/subdir/grandparent-dir-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo HEAD:dir/subdir/parent-dir-link-to-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/ links' '
+	echo dangling 17 >expect &&
+	echo HEAD:dirlink/morx >>expect &&
+	echo HEAD:dirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo $hello_sha1 blob $hello_size >expect &&
+	echo HEAD:dirlink/ind1 | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir/subdir links' '
+	echo dangling 20 >expect &&
+	echo HEAD:subdirlink/morx >>expect &&
+	echo HEAD:subdirlink/morx | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:subdirlink/ind2 | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for dir ->subdir links' '
+	echo notdir 27 >expect &&
+	echo HEAD:dir/link-to-child/morx >>expect &&
+	echo HEAD:dir/link-to-child/morx | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:dir/link-to-child | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo HEAD:link-to-down-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks' '
+	echo symlink 8 >expect &&
+	echo ../fleem >>expect &&
+	echo HEAD:out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo symlink 2 >expect &&
+	echo .. >>expect &&
+	echo HEAD:out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in dirs' '
+	echo symlink 9 >expect &&
+	echo ../escape >>expect &&
+	echo HEAD:dir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo symlink 2 >expect &&
+	echo .. >>expect &&
+	echo HEAD:dir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for out-of-repo symlinks in subdirs' '
+	echo symlink 15 >expect &&
+	echo ../great-escape >>expect &&
+	echo HEAD:dir/subdir/out-of-repo-link | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo symlink 2 >expect &&
+	echo .. >>expect &&
+	echo HEAD:dir/subdir/out-of-repo-link-dir | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo symlink 3 >expect &&
+	echo ../ >>expect &&
+	echo HEAD:dir/subdir/out-of-repo-link-dir-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlinks works for symlinks with internal ..' '
+	echo HEAD: | git cat-file --batch-check >expect &&
+	echo HEAD:up-down | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:up-down-trailing | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual &&
+	echo symlink 7 >expect &&
+	echo ../morx >>expect &&
+	echo HEAD:up-up-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual &&
+	echo HEAD:up-two-down-file | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp found actual
+'
+
+test_expect_success 'git cat-file --batch-check --follow-symlink breaks loops' '
+	echo loop 10 >expect &&
+	echo HEAD:loop1 >>expect &&
+	echo HEAD:loop1 | git cat-file --batch-check --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git cat-file --batch --follow-symlink returns correct sha and mode' '
+	echo HEAD:morx | git cat-file --batch >expect &&
+	echo HEAD:morx | git cat-file --batch --follow-symlinks >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index ba89f4c..d787bf5 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -519,7 +519,7 @@
 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 ref '"'"'$c'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -555,7 +555,7 @@
 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 ref '"'"'$a'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -688,7 +688,7 @@
 	update $c  ''
 	EOF
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: Cannot lock ref '"'"'$c'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual &&
@@ -883,7 +883,7 @@
 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 ref '"'"'$c'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -899,7 +899,7 @@
 	git rev-parse "$c" >expect &&
 	printf $F "create $c" "$m~1" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: Cannot lock ref '"'"'$c'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
 	git rev-parse "$c" >actual &&
 	test_cmp expect actual
 '
@@ -930,7 +930,7 @@
 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 ref '"'"'$a'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -1045,7 +1045,7 @@
 	git update-ref $c $m &&
 	printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: Cannot lock ref '"'"'$c'"'"'" err &&
+	grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual &&
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index 468e856..16d0b8b 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -68,6 +68,14 @@
 	test_cmp expect .git/my-private-file
 '
 
+test_expect_success 'branch -D cannot delete ref in .git dir' '
+	git rev-parse HEAD >.git/my-private-file &&
+	git rev-parse HEAD >expect &&
+	git branch foo/legit &&
+	test_must_fail git branch -D foo////./././../../../my-private-file &&
+	test_cmp expect .git/my-private-file
+'
+
 test_expect_success 'branch -D cannot delete absolute path' '
 	git branch -f extra &&
 	test_must_fail git branch -D "$(pwd)/.git/refs/heads/extra" &&
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
index 8f36aa9..cc5b870 100755
--- a/t/t1501-worktree.sh
+++ b/t/t1501-worktree.sh
@@ -346,4 +346,81 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'Multi-worktree setup' '
+	mkdir work &&
+	mkdir -p repo.git/repos/foo &&
+	cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
+	test_might_fail cp repo.git/sharedindex.* repo.git/repos/foo &&
+	sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'GIT_DIR set (1)' '
+	echo "gitdir: repo.git/repos/foo" >gitfile &&
+	echo ../.. >repo.git/repos/foo/commondir &&
+	(
+		cd work &&
+		GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+		test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'GIT_DIR set (2)' '
+	echo "gitdir: repo.git/repos/foo" >gitfile &&
+	echo "$(pwd)/repo.git" >repo.git/repos/foo/commondir &&
+	(
+		cd work &&
+		GIT_DIR=../gitfile git rev-parse --git-common-dir >actual &&
+		test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'Auto discovery' '
+	echo "gitdir: repo.git/repos/foo" >.git &&
+	echo ../.. >repo.git/repos/foo/commondir &&
+	(
+		cd work &&
+		git rev-parse --git-common-dir >actual &&
+		test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+		test_cmp expect actual &&
+		echo haha >data1 &&
+		git add data1 &&
+		git ls-files --full-name :/ | grep data1 >actual &&
+		echo work/data1 >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '$GIT_DIR/common overrides core.worktree' '
+	mkdir elsewhere &&
+	git --git-dir=repo.git config core.worktree "$TRASH_DIRECTORY/elsewhere" &&
+	echo "gitdir: repo.git/repos/foo" >.git &&
+	echo ../.. >repo.git/repos/foo/commondir &&
+	(
+		cd work &&
+		git rev-parse --git-common-dir >actual &&
+		test-path-utils real_path "$TRASH_DIRECTORY/repo.git" >expect &&
+		test_cmp expect actual &&
+		echo haha >data2 &&
+		git add data2 &&
+		git ls-files --full-name :/ | grep data2 >actual &&
+		echo work/data2 >expect &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' '
+	echo "gitdir: repo.git/repos/foo" >.git &&
+	echo ../.. >repo.git/repos/foo/commondir &&
+	(
+		cd work &&
+		echo haha >data3 &&
+		git --git-dir=../.git --work-tree=. add data3 &&
+		git ls-files --full-name -- :/ | grep data3 >actual &&
+		echo data3 >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 1978947..46ef1f2 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -150,7 +150,7 @@
 
 test_expect_success 'branch@{u} error message when no upstream' '
 	cat >expect <<-EOF &&
-	fatal: No upstream configured for branch ${sq}non-tracking${sq}
+	fatal: no upstream configured for branch ${sq}non-tracking${sq}
 	EOF
 	error_message non-tracking@{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -158,7 +158,7 @@
 
 test_expect_success '@{u} error message when no upstream' '
 	cat >expect <<-EOF &&
-	fatal: No upstream configured for branch ${sq}master${sq}
+	fatal: no upstream configured for branch ${sq}master${sq}
 	EOF
 	test_must_fail git rev-parse --verify @{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -166,7 +166,7 @@
 
 test_expect_success 'branch@{u} error message with misspelt branch' '
 	cat >expect <<-EOF &&
-	fatal: No such branch: ${sq}no-such-branch${sq}
+	fatal: no such branch: ${sq}no-such-branch${sq}
 	EOF
 	error_message no-such-branch@{u} 2>actual &&
 	test_i18ncmp expect actual
@@ -183,7 +183,7 @@
 
 test_expect_success 'branch@{u} error message if upstream branch not fetched' '
 	cat >expect <<-EOF &&
-	fatal: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
+	fatal: upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
 	EOF
 	error_message bad-upstream@{u} 2>actual &&
 	test_i18ncmp expect actual
diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh
index f6aa3c7..13ae12d 100755
--- a/t/t1510-repo-setup.sh
+++ b/t/t1510-repo-setup.sh
@@ -106,6 +106,7 @@
 expect () {
 	cat >"$1/expected" <<-EOF
 	setup: git_dir: $2
+	setup: git_common_dir: $2
 	setup: worktree: $3
 	setup: cwd: $4
 	setup: prefix: $5
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
new file mode 100755
index 0000000..7214f5b
--- /dev/null
+++ b/t/t1514-rev-parse-push.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='test <branch>@{push} syntax'
+. ./test-lib.sh
+
+resolve () {
+	echo "$2" >expect &&
+	git rev-parse --symbolic-full-name "$1" >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'setup' '
+	git init --bare parent.git &&
+	git init --bare other.git &&
+	git remote add origin parent.git &&
+	git remote add other other.git &&
+	test_commit base &&
+	git push origin HEAD &&
+	git branch --set-upstream-to=origin/master master &&
+	git branch --track topic origin/master &&
+	git push origin topic &&
+	git push other topic
+'
+
+test_expect_success '@{push} with default=nothing' '
+	test_config push.default nothing &&
+	test_must_fail git rev-parse master@{push}
+'
+
+test_expect_success '@{push} with default=simple' '
+	test_config push.default simple &&
+	resolve master@{push} refs/remotes/origin/master
+'
+
+test_expect_success 'triangular @{push} fails with default=simple' '
+	test_config push.default simple &&
+	test_must_fail git rev-parse topic@{push}
+'
+
+test_expect_success '@{push} with default=current' '
+	test_config push.default current &&
+	resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with default=matching' '
+	test_config push.default matching &&
+	resolve topic@{push} refs/remotes/origin/topic
+'
+
+test_expect_success '@{push} with pushremote defined' '
+	test_config push.default current &&
+	test_config branch.topic.pushremote other &&
+	resolve topic@{push} refs/remotes/other/topic
+'
+
+test_expect_success '@{push} with push refspecs' '
+	test_config push.default nothing &&
+	test_config remote.origin.push refs/heads/*:refs/heads/magic/* &&
+	git push &&
+	resolve topic@{push} refs/remotes/origin/magic/topic
+'
+
+test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
index b99d519..8396320 100755
--- a/t/t2019-checkout-ambiguous-ref.sh
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -56,4 +56,30 @@
 	test_i18ngrep ! "^HEAD is now at" stderr
 '
 
+test_expect_success 'wildcard ambiguation, paths win' '
+	git init ambi &&
+	(
+		cd ambi &&
+		echo a >a.c &&
+		git add a.c &&
+		echo b >a.c &&
+		git checkout "*.c" &&
+		echo a >expect &&
+		test_cmp expect a.c
+	)
+'
+
+test_expect_success 'wildcard ambiguation, refs lose' '
+	git init ambi2 &&
+	(
+		cd ambi2 &&
+		echo a >"*.c" &&
+		git add . &&
+		test_must_fail git show :"*.c" &&
+		git show :"*.c" -- >actual &&
+		echo a >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_done
diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh
new file mode 100755
index 0000000..f8e4df48
--- /dev/null
+++ b/t/t2025-checkout-to.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+test_description='test git checkout --to'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	test_commit init
+'
+
+test_expect_success 'checkout --to not updating paths' '
+	test_must_fail git checkout --to -- init.t
+'
+
+test_expect_success 'checkout --to an existing worktree' '
+	mkdir -p existing/subtree &&
+	test_must_fail git checkout --detach --to existing master
+'
+
+test_expect_success 'checkout --to an existing empty worktree' '
+	mkdir existing_empty &&
+	git checkout --detach --to existing_empty master
+'
+
+test_expect_success 'checkout --to refuses to checkout locked branch' '
+	test_must_fail git checkout --to zere master &&
+	! test -d zere &&
+	! test -d .git/worktrees/zere
+'
+
+test_expect_success 'checkout --to a new worktree' '
+	git rev-parse HEAD >expect &&
+	git checkout --detach --to here master &&
+	(
+		cd here &&
+		test_cmp ../init.t init.t &&
+		test_must_fail git symbolic-ref HEAD &&
+		git rev-parse HEAD >actual &&
+		test_cmp ../expect actual &&
+		git fsck
+	)
+'
+
+test_expect_success 'checkout --to a new worktree from a subdir' '
+	(
+		mkdir sub &&
+		cd sub &&
+		git checkout --detach --to here master &&
+		cd here &&
+		test_cmp ../../init.t init.t
+	)
+'
+
+test_expect_success 'checkout --to from a linked checkout' '
+	(
+		cd here &&
+		git checkout --detach --to nested-here master &&
+		cd nested-here &&
+		git fsck
+	)
+'
+
+test_expect_success 'checkout --to a new worktree creating new branch' '
+	git checkout --to there -b newmaster master &&
+	(
+		cd there &&
+		test_cmp ../init.t init.t &&
+		git symbolic-ref HEAD >actual &&
+		echo refs/heads/newmaster >expect &&
+		test_cmp expect actual &&
+		git fsck
+	)
+'
+
+test_expect_success 'die the same branch is already checked out' '
+	(
+		cd here &&
+		test_must_fail git checkout newmaster
+	)
+'
+
+test_expect_success 'not die the same branch is already checked out' '
+	(
+		cd here &&
+		git checkout --ignore-other-worktrees --to anothernewmaster newmaster
+	)
+'
+
+test_expect_success 'not die on re-checking out current branch' '
+	(
+		cd there &&
+		git checkout newmaster
+	)
+'
+
+test_expect_success 'checkout --to from a bare repo' '
+	(
+		git clone --bare . bare &&
+		cd bare &&
+		git checkout --to ../there2 -b bare-master master
+	)
+'
+
+test_expect_success 'checkout from a bare repo without --to' '
+	(
+		cd bare &&
+		test_must_fail git checkout master
+	)
+'
+
+test_expect_success 'checkout with grafts' '
+	test_when_finished rm .git/info/grafts &&
+	test_commit abc &&
+	SHA1=`git rev-parse HEAD` &&
+	test_commit def &&
+	test_commit xyz &&
+	echo "`git rev-parse HEAD` $SHA1" >.git/info/grafts &&
+	cat >expected <<-\EOF &&
+	xyz
+	abc
+	EOF
+	git log --format=%s -2 >actual &&
+	test_cmp expected actual &&
+	git checkout --detach --to grafted master &&
+	git --git-dir=grafted/.git log --format=%s -2 >actual &&
+	test_cmp expected actual
+'
+
+test_done
diff --git a/t/t2026-prune-linked-checkouts.sh b/t/t2026-prune-linked-checkouts.sh
new file mode 100755
index 0000000..1821a48
--- /dev/null
+++ b/t/t2026-prune-linked-checkouts.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='prune $GIT_DIR/worktrees'
+
+. ./test-lib.sh
+
+test_expect_success initialize '
+	git commit --allow-empty -m init
+'
+
+test_expect_success 'prune --worktrees on normal repo' '
+	git prune --worktrees &&
+	test_must_fail git prune --worktrees abc
+'
+
+test_expect_success 'prune files inside $GIT_DIR/worktrees' '
+	mkdir .git/worktrees &&
+	: >.git/worktrees/abc &&
+	git prune --worktrees --verbose >actual &&
+	cat >expect <<EOF &&
+Removing worktrees/abc: not a valid directory
+EOF
+	test_i18ncmp expect actual &&
+	! test -f .git/worktrees/abc &&
+	! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories without gitdir' '
+	mkdir -p .git/worktrees/def/abc &&
+	: >.git/worktrees/def/def &&
+	cat >expect <<EOF &&
+Removing worktrees/def: gitdir file does not exist
+EOF
+	git prune --worktrees --verbose >actual &&
+	test_i18ncmp expect actual &&
+	! test -d .git/worktrees/def &&
+	! test -d .git/worktrees
+'
+
+test_expect_success SANITY 'prune directories with unreadable gitdir' '
+	mkdir -p .git/worktrees/def/abc &&
+	: >.git/worktrees/def/def &&
+	: >.git/worktrees/def/gitdir &&
+	chmod u-r .git/worktrees/def/gitdir &&
+	git prune --worktrees --verbose >actual &&
+	test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
+	! test -d .git/worktrees/def &&
+	! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories with invalid gitdir' '
+	mkdir -p .git/worktrees/def/abc &&
+	: >.git/worktrees/def/def &&
+	: >.git/worktrees/def/gitdir &&
+	git prune --worktrees --verbose >actual &&
+	test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
+	! test -d .git/worktrees/def &&
+	! test -d .git/worktrees
+'
+
+test_expect_success 'prune directories with gitdir pointing to nowhere' '
+	mkdir -p .git/worktrees/def/abc &&
+	: >.git/worktrees/def/def &&
+	echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
+	git prune --worktrees --verbose >actual &&
+	test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
+	! test -d .git/worktrees/def &&
+	! test -d .git/worktrees
+'
+
+test_expect_success 'not prune locked checkout' '
+	test_when_finished rm -r .git/worktrees &&
+	mkdir -p .git/worktrees/ghi &&
+	: >.git/worktrees/ghi/locked &&
+	git prune --worktrees &&
+	test -d .git/worktrees/ghi
+'
+
+test_expect_success 'not prune recent checkouts' '
+	test_when_finished rm -r .git/worktrees &&
+	mkdir zz &&
+	mkdir -p .git/worktrees/jlm &&
+	echo "$(pwd)"/zz >.git/worktrees/jlm/gitdir &&
+	rmdir zz &&
+	git prune --worktrees --verbose --expire=2.days.ago &&
+	test -d .git/worktrees/jlm
+'
+
+test_expect_success 'not prune proper checkouts' '
+	test_when_finished rm -r .git/worktrees &&
+	git checkout "--to=$PWD/nop" --detach master &&
+	git prune --worktrees &&
+	test -d .git/worktrees/nop
+'
+
+test_done
diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh
new file mode 100755
index 0000000..46aadc4
--- /dev/null
+++ b/t/t3033-merge-toplevel.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+test_description='"git merge" top-level frontend'
+
+. ./test-lib.sh
+
+t3033_reset () {
+	git checkout -B master two &&
+	git branch -f left three &&
+	git branch -f right four
+}
+
+test_expect_success setup '
+	test_commit one &&
+	git branch left &&
+	git branch right &&
+	test_commit two &&
+	git checkout left &&
+	test_commit three &&
+	git checkout right &&
+	test_commit four &&
+	git checkout master
+'
+
+# Local branches
+
+test_expect_success 'merge an octopus into void' '
+	t3033_reset &&
+	git checkout --orphan test &&
+	git rm -fr . &&
+	test_must_fail git merge left right &&
+	test_must_fail git rev-parse --verify HEAD &&
+	git diff --quiet &&
+	test_must_fail git rev-parse HEAD
+'
+
+test_expect_success 'merge an octopus, fast-forward (ff)' '
+	t3033_reset &&
+	git reset --hard one &&
+	git merge left right &&
+	# one is ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^3 &&
+	git rev-parse HEAD^1 HEAD^2 | sort >actual &&
+	git rev-parse three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, non-fast-forward (ff)' '
+	t3033_reset &&
+	git reset --hard one &&
+	git merge --no-ff left right &&
+	# one is ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse one three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, fast-forward (does not ff)' '
+	t3033_reset &&
+	git merge left right &&
+	# two (master) is not an ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse two three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge octopus, non-fast-forward' '
+	t3033_reset &&
+	git merge --no-ff left right &&
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse two three four | sort >expect &&
+	test_cmp expect actual
+'
+
+# The same set with FETCH_HEAD
+
+test_expect_success 'merge FETCH_HEAD octopus into void' '
+	t3033_reset &&
+	git checkout --orphan test &&
+	git rm -fr . &&
+	git fetch . left right &&
+	test_must_fail git merge FETCH_HEAD &&
+	test_must_fail git rev-parse --verify HEAD &&
+	git diff --quiet &&
+	test_must_fail git rev-parse HEAD
+'
+
+test_expect_success 'merge FETCH_HEAD octopus fast-forward (ff)' '
+	t3033_reset &&
+	git reset --hard one &&
+	git fetch . left right &&
+	git merge FETCH_HEAD &&
+	# one is ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^3 &&
+	git rev-parse HEAD^1 HEAD^2 | sort >actual &&
+	git rev-parse three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus non-fast-forward (ff)' '
+	t3033_reset &&
+	git reset --hard one &&
+	git fetch . left right &&
+	git merge --no-ff FETCH_HEAD &&
+	# one is ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse one three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus fast-forward (does not ff)' '
+	t3033_reset &&
+	git fetch . left right &&
+	git merge FETCH_HEAD &&
+	# two (master) is not an ancestor of three (left) and four (right)
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse two three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' '
+	t3033_reset &&
+	git fetch . left right &&
+	git merge --no-ff FETCH_HEAD &&
+	test_must_fail git rev-parse --verify HEAD^4 &&
+	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
+	git rev-parse two three four | sort >expect &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index aa9eb3a..8aae98d 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -187,4 +187,21 @@
 	test_must_fail git branch foo/bar/baz/lots/of/extra/components
 '
 
+test_expect_success 'timeout if packed-refs.lock exists' '
+	LOCK=.git/packed-refs.lock &&
+	>"$LOCK" &&
+	test_when_finished "rm -f $LOCK" &&
+	test_must_fail git pack-refs --all --prune
+'
+
+test_expect_success 'retry acquiring packed-refs.lock' '
+	LOCK=.git/packed-refs.lock &&
+	>"$LOCK" &&
+	test_when_finished "wait; rm -f $LOCK" &&
+	{
+		( sleep 1 ; rm -f $LOCK ) &
+	} &&
+	git -c core.packedrefstimeout=3000 pack-refs --all --prune
+'
+
 test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 5a27ec9..8f64505 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -47,7 +47,7 @@
 '
 
 test_expect_success 'reference merge' '
-	git merge -s recursive "reference merge" HEAD master
+	git merge -s recursive -m "reference merge" master
 '
 
 PRE_REBASE=$(git rev-parse test-rebase)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ac429a0..467e6c1 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1102,4 +1102,25 @@
 	test $(git cat-file commit HEAD | sed -ne \$p) = I
 '
 
+test_expect_success 'rebase --continue removes CHERRY_PICK_HEAD' '
+	git checkout -b commit-to-skip &&
+	for double in X 3 1
+	do
+		test_seq 5 | sed "s/$double/&&/" >seq &&
+		git add seq &&
+		test_tick &&
+		git commit -m seq-$double
+	done &&
+	git tag seq-onto &&
+	git reset --hard HEAD~2 &&
+	git cherry-pick seq-onto &&
+	set_fake_editor &&
+	test_must_fail env FAKE_LINES= git rebase -i seq-onto &&
+	test -d .git/rebase-merge &&
+	git rebase --continue &&
+	git diff --exit-code seq-onto &&
+	test ! -d .git/rebase-merge &&
+	test ! -f .git/CHERRY_PICK_HEAD
+'
+
 test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 24ddd8a..deae948 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -326,15 +326,34 @@
 	# 2. Correct version applies the (not)edited version, and asks
 	#    about the next hunk, against which we say q and program
 	#    exits.
-	for a in s e     q n q q
-	do
-		echo $a
-	done |
+	printf "%s\n" s e     q n q q |
 	EDITOR=: git add -p &&
 	git diff >actual &&
 	! grep "^+15" actual
 '
 
+test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
+	cat >test <<-\EOF &&
+	5
+	10
+	20
+	21
+	30
+	31
+	40
+	50
+	60
+	EOF
+	git reset &&
+	# test sequence is s(plit), n(o), y(es), e(dit)
+	# q n q q is there to make sure we exit at the end.
+	printf "%s\n" s n y e   q n q q |
+	EDITOR=: git add -p 2>error &&
+	test_must_be_empty error &&
+	git diff >actual &&
+	! grep "^+31" actual
+'
+
 test_expect_success 'patch mode ignores unmerged entries' '
 	git reset --hard &&
 	test_commit conflict &&
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
index 70655c1..38e7300 100755
--- a/t/t3904-stash-patch.sh
+++ b/t/t3904-stash-patch.sh
@@ -1,9 +1,15 @@
 #!/bin/sh
 
-test_description='git checkout --patch'
+test_description='stash -p'
 . ./lib-patch-mode.sh
 
-test_expect_success PERL 'setup' '
+if ! test_have_prereq PERL
+then
+	skip_all='skipping stash -p tests, perl not available'
+	test_done
+fi
+
+test_expect_success 'setup' '
 	mkdir dir &&
 	echo parent > dir/foo &&
 	echo dummy > bar &&
@@ -20,7 +26,7 @@
 
 # note: order of files with unstaged changes: HEAD bar dir/foo
 
-test_expect_success PERL 'saying "n" does nothing' '
+test_expect_success 'saying "n" does nothing' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state dir/foo work index &&
 	(echo n; echo n; echo n) | test_must_fail git stash save -p &&
@@ -29,7 +35,7 @@
 	verify_state dir/foo work index
 '
 
-test_expect_success PERL 'git stash -p' '
+test_expect_success 'git stash -p' '
 	(echo y; echo n; echo y) | git stash save -p &&
 	verify_state HEAD committed HEADfile_index &&
 	verify_saved_state bar &&
@@ -41,7 +47,7 @@
 	verify_state dir/foo work head
 '
 
-test_expect_success PERL 'git stash -p --no-keep-index' '
+test_expect_success 'git stash -p --no-keep-index' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state bar bar_work bar_index &&
 	set_state dir/foo work index &&
@@ -56,7 +62,7 @@
 	verify_state dir/foo work index
 '
 
-test_expect_success PERL 'git stash --no-keep-index -p' '
+test_expect_success 'git stash --no-keep-index -p' '
 	set_state HEAD HEADfile_work HEADfile_index &&
 	set_state bar bar_work bar_index &&
 	set_state dir/foo work index &&
@@ -71,8 +77,31 @@
 	verify_state dir/foo work index
 '
 
-test_expect_success PERL 'none of this moved HEAD' '
+test_expect_success 'none of this moved HEAD' '
 	verify_saved_head
 '
 
+test_expect_failure 'stash -p with split hunk' '
+	git reset --hard &&
+	cat >test <<-\EOF &&
+	aaa
+	bbb
+	ccc
+	EOF
+	git add test &&
+	git commit -m "initial" &&
+	cat >test <<-\EOF &&
+	aaa
+	added line 1
+	bbb
+	added line 2
+	ccc
+	EOF
+	printf "%s\n" s n y q |
+	test_might_fail git stash -p 2>error &&
+	! test_must_be_empty error &&
+	grep "added line 1" test &&
+	! grep "added line 2" test
+'
+
 test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index c39e500..890db11 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -57,6 +57,14 @@
 
 '
 
+test_expect_success "format-patch --ignore-if-in-upstream handles tags" '
+	git tag -a v1 -m tag side &&
+	git tag -a v2 -m tag master &&
+	git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 &&
+	cnt=$(grep "^From " patch1 | wc -l) &&
+	test $cnt = 2
+'
+
 test_expect_success "format-patch doesn't consider merge commits" '
 
 	git checkout -b slave master &&
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 604a838..2434157 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -9,138 +9,144 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-# Ray Lehtiniemi's example
+test_expect_success "Ray Lehtiniemi's example" '
+	cat <<-\EOF >x &&
+	do {
+	   nothing;
+	} while (0);
+	EOF
+	git update-index --add x &&
 
-cat << EOF > x
-do {
-   nothing;
-} while (0);
-EOF
+	cat <<-\EOF >x &&
+	do
+	{
+	   nothing;
+	}
+	while (0);
+	EOF
 
-git update-index --add x
+	cat <<-\EOF >expect &&
+	diff --git a/x b/x
+	index adf3937..6edc172 100644
+	--- a/x
+	+++ b/x
+	@@ -1,3 +1,5 @@
+	-do {
+	+do
+	+{
+	    nothing;
+	-} while (0);
+	+}
+	+while (0);
+	EOF
 
-cat << EOF > x
-do
-{
-   nothing;
-}
-while (0);
-EOF
+	git diff >out &&
+	test_cmp expect out &&
 
-cat << EOF > expect
-diff --git a/x b/x
-index adf3937..6edc172 100644
---- a/x
-+++ b/x
-@@ -1,3 +1,5 @@
--do {
-+do
-+{
-    nothing;
--} while (0);
-+}
-+while (0);
-EOF
+	git diff -w >out &&
+	test_cmp expect out &&
 
-git diff > out
-test_expect_success "Ray's example without options" 'test_cmp expect out'
+	git diff -b >out &&
+	test_cmp expect out
+'
 
-git diff -w > out
-test_expect_success "Ray's example with -w" 'test_cmp expect out'
-
-git diff -b > out
-test_expect_success "Ray's example with -b" 'test_cmp expect out'
-
-tr 'Q' '\015' << EOF > x
-whitespace at beginning
-whitespace change
-whitespace in the middle
-whitespace at end
-unchanged line
-CR at endQ
-EOF
-
-git update-index x
-
-tr '_' ' ' << EOF > x
+test_expect_success 'another test, without options' '
+	tr Q "\015" <<-\EOF >x &&
 	whitespace at beginning
-whitespace 	 change
-white space in the middle
-whitespace at end__
-unchanged line
-CR at end
-EOF
+	whitespace change
+	whitespace in the middle
+	whitespace at end
+	unchanged line
+	CR at endQ
+	EOF
 
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
--whitespace at end
-+	whitespace at beginning
-+whitespace 	 change
-+white space in the middle
-+whitespace at end__
- unchanged line
--CR at endQ
-+CR at end
-EOF
-git diff > out
-test_expect_success 'another test, without options' 'test_cmp expect out'
+	git update-index x &&
 
-cat << EOF > expect
-EOF
-git diff -w > out
-test_expect_success 'another test, with -w' 'test_cmp expect out'
-git diff -w -b > out
-test_expect_success 'another test, with -w -b' 'test_cmp expect out'
-git diff -w --ignore-space-at-eol > out
-test_expect_success 'another test, with -w --ignore-space-at-eol' 'test_cmp expect out'
-git diff -w -b --ignore-space-at-eol > out
-test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out'
+	tr "_" " " <<-\EOF >x &&
+	_	whitespace at beginning
+	whitespace 	 change
+	white space in the middle
+	whitespace at end__
+	unchanged line
+	CR at end
+	EOF
 
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
-+	whitespace at beginning
- whitespace 	 change
--whitespace in the middle
-+white space in the middle
- whitespace at end__
- unchanged line
- CR at end
-EOF
-git diff -b > out
-test_expect_success 'another test, with -b' 'test_cmp expect out'
-git diff -b --ignore-space-at-eol > out
-test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
+	tr "Q_" "\015 " <<-\EOF >expect &&
+	diff --git a/x b/x
+	index d99af23..22d9f73 100644
+	--- a/x
+	+++ b/x
+	@@ -1,6 +1,6 @@
+	-whitespace at beginning
+	-whitespace change
+	-whitespace in the middle
+	-whitespace at end
+	+ 	whitespace at beginning
+	+whitespace 	 change
+	+white space in the middle
+	+whitespace at end__
+	 unchanged line
+	-CR at endQ
+	+CR at end
+	EOF
 
-tr 'Q_' '\015 ' << EOF > expect
-diff --git a/x b/x
-index d99af23..8b32fb5 100644
---- a/x
-+++ b/x
-@@ -1,6 +1,6 @@
--whitespace at beginning
--whitespace change
--whitespace in the middle
-+	whitespace at beginning
-+whitespace 	 change
-+white space in the middle
- whitespace at end__
- unchanged line
- CR at end
-EOF
-git diff --ignore-space-at-eol > out
-test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
+	git diff >out &&
+	test_cmp expect out &&
+
+	>expect &&
+	git diff -w >out &&
+	test_cmp expect out &&
+
+	git diff -w -b >out &&
+	test_cmp expect out &&
+
+	git diff -w --ignore-space-at-eol >out &&
+	test_cmp expect out &&
+
+	git diff -w -b --ignore-space-at-eol >out &&
+	test_cmp expect out &&
+
+
+	tr "Q_" "\015 " <<-\EOF >expect &&
+	diff --git a/x b/x
+	index d99af23..22d9f73 100644
+	--- a/x
+	+++ b/x
+	@@ -1,6 +1,6 @@
+	-whitespace at beginning
+	+_	whitespace at beginning
+	 whitespace 	 change
+	-whitespace in the middle
+	+white space in the middle
+	 whitespace at end__
+	 unchanged line
+	 CR at end
+	EOF
+	git diff -b >out &&
+	test_cmp expect out &&
+
+	git diff -b --ignore-space-at-eol >out &&
+	test_cmp expect out &&
+
+	tr "Q_" "\015 " <<-\EOF >expect &&
+	diff --git a/x b/x
+	index d99af23..22d9f73 100644
+	--- a/x
+	+++ b/x
+	@@ -1,6 +1,6 @@
+	-whitespace at beginning
+	-whitespace change
+	-whitespace in the middle
+	+_	whitespace at beginning
+	+whitespace 	 change
+	+white space in the middle
+	 whitespace at end__
+	 unchanged line
+	 CR at end
+	EOF
+	git diff --ignore-space-at-eol >out &&
+	test_cmp expect out
+'
 
 test_expect_success 'ignore-blank-lines: only new lines' '
 	test_seq 5 >x &&
@@ -489,291 +495,219 @@
 '
 
 test_expect_success 'check mixed spaces and tabs in indent' '
-
 	# This is indented with SP HT SP.
-	echo " 	 foo();" > x &&
+	echo " 	 foo();" >x &&
 	git diff --check | grep "space before tab in indent"
-
 '
 
 test_expect_success 'check mixed tabs and spaces in indent' '
-
 	# This is indented with HT SP HT.
-	echo "	 	foo();" > x &&
+	echo "	 	foo();" >x &&
 	git diff --check | grep "space before tab in indent"
-
 '
 
 test_expect_success 'check with no whitespace errors' '
-
 	git commit -m "snapshot" &&
-	echo "foo();" > x &&
+	echo "foo();" >x &&
 	git diff --check
-
 '
 
 test_expect_success 'check with trailing whitespace' '
-
-	echo "foo(); " > x &&
+	echo "foo(); " >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check with space before tab in indent' '
-
 	# indent has space followed by hard tab
-	echo " 	foo();" > x &&
+	echo " 	foo();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success '--check and --exit-code are not exclusive' '
-
 	git checkout x &&
 	git diff --check --exit-code
-
 '
 
 test_expect_success '--check and --quiet are not exclusive' '
-
 	git diff --check --quiet
-
 '
 
 test_expect_success 'check staged with no whitespace errors' '
-
-	echo "foo();" > x &&
+	echo "foo();" >x &&
 	git add x &&
 	git diff --cached --check
-
 '
 
 test_expect_success 'check staged with trailing whitespace' '
-
-	echo "foo(); " > x &&
+	echo "foo(); " >x &&
 	git add x &&
 	test_must_fail git diff --cached --check
-
 '
 
 test_expect_success 'check staged with space before tab in indent' '
-
 	# indent has space followed by hard tab
-	echo " 	foo();" > x &&
+	echo " 	foo();" >x &&
 	git add x &&
 	test_must_fail git diff --cached --check
-
 '
 
 test_expect_success 'check with no whitespace errors (diff-index)' '
-
-	echo "foo();" > x &&
+	echo "foo();" >x &&
 	git add x &&
 	git diff-index --check HEAD
-
 '
 
 test_expect_success 'check with trailing whitespace (diff-index)' '
-
-	echo "foo(); " > x &&
+	echo "foo(); " >x &&
 	git add x &&
 	test_must_fail git diff-index --check HEAD
-
 '
 
 test_expect_success 'check with space before tab in indent (diff-index)' '
-
 	# indent has space followed by hard tab
-	echo " 	foo();" > x &&
+	echo " 	foo();" >x &&
 	git add x &&
 	test_must_fail git diff-index --check HEAD
-
 '
 
 test_expect_success 'check staged with no whitespace errors (diff-index)' '
-
-	echo "foo();" > x &&
+	echo "foo();" >x &&
 	git add x &&
 	git diff-index --cached --check HEAD
-
 '
 
 test_expect_success 'check staged with trailing whitespace (diff-index)' '
-
-	echo "foo(); " > x &&
+	echo "foo(); " >x &&
 	git add x &&
 	test_must_fail git diff-index --cached --check HEAD
-
 '
 
 test_expect_success 'check staged with space before tab in indent (diff-index)' '
-
 	# indent has space followed by hard tab
-	echo " 	foo();" > x &&
+	echo " 	foo();" >x &&
 	git add x &&
 	test_must_fail git diff-index --cached --check HEAD
-
 '
 
 test_expect_success 'check with no whitespace errors (diff-tree)' '
-
-	echo "foo();" > x &&
+	echo "foo();" >x &&
 	git commit -m "new commit" x &&
 	git diff-tree --check HEAD^ HEAD
-
 '
 
 test_expect_success 'check with trailing whitespace (diff-tree)' '
-
-	echo "foo(); " > x &&
+	echo "foo(); " >x &&
 	git commit -m "another commit" x &&
 	test_must_fail git diff-tree --check HEAD^ HEAD
-
 '
 
 test_expect_success 'check with space before tab in indent (diff-tree)' '
-
 	# indent has space followed by hard tab
-	echo " 	foo();" > x &&
+	echo " 	foo();" >x &&
 	git commit -m "yet another" x &&
 	test_must_fail git diff-tree --check HEAD^ HEAD
-
 '
 
 test_expect_success 'check trailing whitespace (trailing-space: off)' '
-
 	git config core.whitespace "-trailing-space" &&
-	echo "foo ();   " > x &&
+	echo "foo ();   " >x &&
 	git diff --check
-
 '
 
 test_expect_success 'check trailing whitespace (trailing-space: on)' '
-
 	git config core.whitespace "trailing-space" &&
-	echo "foo ();   " > x &&
+	echo "foo ();   " >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check space before tab in indent (space-before-tab: off)' '
-
 	# indent contains space followed by HT
 	git config core.whitespace "-space-before-tab" &&
-	echo " 	foo ();" > x &&
+	echo " 	foo ();" >x &&
 	git diff --check
-
 '
 
 test_expect_success 'check space before tab in indent (space-before-tab: on)' '
-
 	# indent contains space followed by HT
 	git config core.whitespace "space-before-tab" &&
-	echo " 	foo ();   " > x &&
+	echo " 	foo ();   " >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
-
 	git config core.whitespace "-indent-with-non-tab" &&
-	echo "        foo ();" > x &&
+	echo "        foo ();" >x &&
 	git diff --check
-
 '
 
 test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
-
 	git config core.whitespace "indent-with-non-tab" &&
-	echo "        foo ();" > x &&
+	echo "        foo ();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'ditto, but tabwidth=9' '
-
 	git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
 	git diff --check
-
 '
 
 test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
-
 	git config core.whitespace "indent-with-non-tab" &&
-	echo "	                foo ();" > x &&
+	echo "	                foo ();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'ditto, but tabwidth=10' '
-
 	git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'ditto, but tabwidth=20' '
-
 	git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
 	git diff --check
-
 '
 
 test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
-
 	git config core.whitespace "-tab-in-indent" &&
-	echo "	foo ();" > x &&
+	echo "	foo ();" >x &&
 	git diff --check
-
 '
 
 test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
-
 	git config core.whitespace "tab-in-indent" &&
-	echo "	foo ();" > x &&
+	echo "	foo ();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
-
 	git config core.whitespace "tab-in-indent" &&
-	echo "	                foo ();" > x &&
+	echo "	                foo ();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
-
 	git config core.whitespace "tab-in-indent,tabwidth=1" &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
-
 	git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
-	echo "foo ();" > x &&
+	echo "foo ();" >x &&
 	test_must_fail git diff --check
-
 '
 
 test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
-
 	git config --unset core.whitespace &&
-	echo "x whitespace" > .gitattributes &&
-	echo "	  foo ();" > x &&
+	echo "x whitespace" >.gitattributes &&
+	echo "	  foo ();" >x &&
 	git diff --check &&
 	rm -f .gitattributes
-
 '
 
 test_expect_success 'line numbers in --check output are correct' '
-
-	echo "" > x &&
-	echo "foo(); " >> x &&
+	echo "" >x &&
+	echo "foo(); " >>x &&
 	git diff --check | grep "x:2:"
-
 '
 
 test_expect_success 'checkdiff detects new trailing blank lines (1)' '
@@ -876,29 +810,127 @@
 	git config color.diff.old red &&
 	git config color.diff.new green &&
 	git config color.diff.commit yellow &&
-	git config color.diff.whitespace "normal red" &&
+	git config color.diff.whitespace blue &&
 
 	git config core.autocrlf false
 '
-cat >expected <<\EOF
-<BOLD>diff --git a/x b/x<RESET>
-<BOLD>index 9daeafb..2874b91 100644<RESET>
-<BOLD>--- a/x<RESET>
-<BOLD>+++ b/x<RESET>
-<CYAN>@@ -1 +1,4 @@<RESET>
- test<RESET>
-<GREEN>+<RESET><GREEN>{<RESET>
-<GREEN>+<RESET><BRED>	<RESET>
-<GREEN>+<RESET><GREEN>}<RESET>
-EOF
 
 test_expect_success 'diff that introduces a line with only tabs' '
 	git config core.whitespace blank-at-eol &&
 	git reset --hard &&
-	echo "test" > x &&
+	echo "test" >x &&
 	git commit -m "initial" x &&
-	echo "{NTN}" | tr "NT" "\n\t" >> x &&
+	echo "{NTN}" | tr "NT" "\n\t" >>x &&
 	git -c color.diff=always diff | test_decode_color >current &&
+
+	cat >expected <<-\EOF &&
+	<BOLD>diff --git a/x b/x<RESET>
+	<BOLD>index 9daeafb..2874b91 100644<RESET>
+	<BOLD>--- a/x<RESET>
+	<BOLD>+++ b/x<RESET>
+	<CYAN>@@ -1 +1,4 @@<RESET>
+	 test<RESET>
+	<GREEN>+<RESET><GREEN>{<RESET>
+	<GREEN>+<RESET><BLUE>	<RESET>
+	<GREEN>+<RESET><GREEN>}<RESET>
+	EOF
+
+	test_cmp expected current
+'
+
+test_expect_success 'diff that introduces and removes ws breakages' '
+	git reset --hard &&
+	{
+		echo "0. blank-at-eol " &&
+		echo "1. blank-at-eol "
+	} >x &&
+	git commit -a --allow-empty -m preimage &&
+	{
+		echo "0. blank-at-eol " &&
+		echo "1. still-blank-at-eol " &&
+		echo "2. and a new line "
+	} >x &&
+
+	git -c color.diff=always diff |
+	test_decode_color >current &&
+
+	cat >expected <<-\EOF &&
+	<BOLD>diff --git a/x b/x<RESET>
+	<BOLD>index d0233a2..700886e 100644<RESET>
+	<BOLD>--- a/x<RESET>
+	<BOLD>+++ b/x<RESET>
+	<CYAN>@@ -1,2 +1,3 @@<RESET>
+	 0. blank-at-eol <RESET>
+	<RED>-1. blank-at-eol <RESET>
+	<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+	<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+	EOF
+
+	test_cmp expected current
+'
+
+test_expect_success 'the same with --ws-error-highlight' '
+	git reset --hard &&
+	{
+		echo "0. blank-at-eol " &&
+		echo "1. blank-at-eol "
+	} >x &&
+	git commit -a --allow-empty -m preimage &&
+	{
+		echo "0. blank-at-eol " &&
+		echo "1. still-blank-at-eol " &&
+		echo "2. and a new line "
+	} >x &&
+
+	git -c color.diff=always diff --ws-error-highlight=default,old |
+	test_decode_color >current &&
+
+	cat >expected <<-\EOF &&
+	<BOLD>diff --git a/x b/x<RESET>
+	<BOLD>index d0233a2..700886e 100644<RESET>
+	<BOLD>--- a/x<RESET>
+	<BOLD>+++ b/x<RESET>
+	<CYAN>@@ -1,2 +1,3 @@<RESET>
+	 0. blank-at-eol <RESET>
+	<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+	<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+	<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+	EOF
+
+	test_cmp expected current &&
+
+	git -c color.diff=always diff --ws-error-highlight=all |
+	test_decode_color >current &&
+
+	cat >expected <<-\EOF &&
+	<BOLD>diff --git a/x b/x<RESET>
+	<BOLD>index d0233a2..700886e 100644<RESET>
+	<BOLD>--- a/x<RESET>
+	<BOLD>+++ b/x<RESET>
+	<CYAN>@@ -1,2 +1,3 @@<RESET>
+	 <RESET>0. blank-at-eol<RESET><BLUE> <RESET>
+	<RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET>
+	<GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET>
+	<GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
+	EOF
+
+	test_cmp expected current &&
+
+	git -c color.diff=always diff --ws-error-highlight=none |
+	test_decode_color >current &&
+
+	cat >expected <<-\EOF &&
+	<BOLD>diff --git a/x b/x<RESET>
+	<BOLD>index d0233a2..700886e 100644<RESET>
+	<BOLD>--- a/x<RESET>
+	<BOLD>+++ b/x<RESET>
+	<CYAN>@@ -1,2 +1,3 @@<RESET>
+	 0. blank-at-eol <RESET>
+	<RED>-1. blank-at-eol <RESET>
+	<GREEN>+1. still-blank-at-eol <RESET>
+	<GREEN>+2. and a new line <RESET>
+	EOF
+
 	test_cmp expected current
 '
 
diff --git a/t/t4136-apply-check.sh b/t/t4136-apply-check.sh
index a321f7c..4b0a374 100755
--- a/t/t4136-apply-check.sh
+++ b/t/t4136-apply-check.sh
@@ -16,4 +16,17 @@
 	EOF
 '
 
+test_expect_success 'apply exits non-zero with no-op patch' '
+	cat >input <<-\EOF &&
+	diff --get a/1 b/1
+	index 6696ea4..606eddd 100644
+	--- a/1
+	+++ b/1
+	@@ -1,1 +1,1 @@
+	 1
+	EOF
+	test_must_fail git apply --stat input &&
+	test_must_fail git apply --check input
+'
+
 test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 306e6f3..b822a39 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -274,15 +274,21 @@
 	grep "^\[foo\] third" actual
 '
 
-test_expect_success 'am -3 falls back to 3-way merge' '
+test_expect_success 'setup am -3' '
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
-	git checkout -b lorem2 master2 &&
+	git checkout -b base3way master2 &&
 	sed -n -e "3,\$p" msg >file &&
 	head -n 9 msg >>file &&
 	git add file &&
 	test_tick &&
-	git commit -m "copied stuff" &&
+	git commit -m "copied stuff"
+'
+
+test_expect_success 'am -3 falls back to 3-way merge' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout -b lorem2 base3way &&
 	git am -3 lorem-move.patch &&
 	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code lorem
@@ -291,17 +297,31 @@
 test_expect_success 'am -3 -p0 can read --no-prefix patch' '
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
-	git checkout -b lorem3 master2 &&
-	sed -n -e "3,\$p" msg >file &&
-	head -n 9 msg >>file &&
-	git add file &&
-	test_tick &&
-	git commit -m "copied stuff" &&
+	git checkout -b lorem3 base3way &&
 	git am -3 -p0 lorem-zero.patch &&
 	test_path_is_missing .git/rebase-apply &&
 	git diff --exit-code lorem
 '
 
+test_expect_success 'am with config am.threeWay falls back to 3-way merge' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout -b lorem4 base3way &&
+	test_config am.threeWay 1 &&
+	git am lorem-move.patch &&
+	test_path_is_missing .git/rebase-apply &&
+	git diff --exit-code lorem
+'
+
+test_expect_success 'am with config am.threeWay overridden by --no-3way' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout -b lorem5 base3way &&
+	test_config am.threeWay 1 &&
+	test_must_fail git am --no-3way lorem-move.patch &&
+	test_path_is_dir .git/rebase-apply
+'
+
 test_expect_success 'am can rename a file' '
 	grep "^rename from" rename.patch &&
 	rm -fr .git/rebase-apply &&
@@ -338,12 +358,7 @@
 test_expect_success 'am -3 -q is quiet' '
 	rm -fr .git/rebase-apply &&
 	git checkout -f lorem2 &&
-	git reset master2 --hard &&
-	sed -n -e "3,\$p" msg >file &&
-	head -n 9 msg >>file &&
-	git add file &&
-	test_tick &&
-	git commit -m "copied stuff" &&
+	git reset base3way --hard &&
 	git am -3 -q lorem-move.patch >output.out 2>&1 &&
 	! test -s output.out
 '
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 8d90634..833e7b2 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -14,6 +14,7 @@
 	git add file-1 file-2 &&
 	git commit -m initial &&
 	git tag initial &&
+	git format-patch --stdout --root initial >initial.patch &&
 	for i in 2 3 4 5 6
 	do
 		echo $i >>file-1 &&
@@ -63,6 +64,28 @@
 
 done
 
+test_expect_success 'am -3 --skip removes otherfile-4' '
+	git reset --hard initial &&
+	test_must_fail git am -3 0003-*.patch &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 4 = "$(cat otherfile-4)" &&
+	git am --skip &&
+	test_cmp_rev initial HEAD &&
+	test -z "$(git ls-files -u)" &&
+	test_path_is_missing otherfile-4
+'
+
+test_expect_success 'am -3 --abort removes otherfile-4' '
+	git reset --hard initial &&
+	test_must_fail git am -3 0003-*.patch &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 4 = "$(cat otherfile-4)" &&
+	git am --abort &&
+	test_cmp_rev initial HEAD &&
+	test -z $(git ls-files -u) &&
+	test_path_is_missing otherfile-4
+'
+
 test_expect_success 'am --abort will keep the local commits intact' '
 	test_must_fail git am 0004-*.patch &&
 	test_commit unrelated &&
@@ -72,4 +95,62 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'am -3 stops on conflict on unborn branch' '
+	git checkout -f --orphan orphan &&
+	git reset &&
+	rm -f otherfile-4 &&
+	test_must_fail git am -3 0003-*.patch &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 4 = "$(cat otherfile-4)"
+'
+
+test_expect_success 'am -3 --skip clears index on unborn branch' '
+	test_path_is_dir .git/rebase-apply &&
+	echo tmpfile >tmpfile &&
+	git add tmpfile &&
+	git am --skip &&
+	test -z "$(git ls-files)" &&
+	test_path_is_missing otherfile-4 &&
+	test_path_is_missing tmpfile
+'
+
+test_expect_success 'am -3 --abort removes otherfile-4 on unborn branch' '
+	git checkout -f --orphan orphan &&
+	git reset &&
+	rm -f otherfile-4 file-1 &&
+	test_must_fail git am -3 0003-*.patch &&
+	test 2 -eq $(git ls-files -u | wc -l) &&
+	test 4 = "$(cat otherfile-4)" &&
+	git am --abort &&
+	test -z "$(git ls-files -u)" &&
+	test_path_is_missing otherfile-4
+'
+
+test_expect_success 'am -3 --abort on unborn branch removes applied commits' '
+	git checkout -f --orphan orphan &&
+	git reset &&
+	rm -f otherfile-4 otherfile-2 file-1 file-2 &&
+	test_must_fail git am -3 initial.patch 0003-*.patch &&
+	test 3 -eq $(git ls-files -u | wc -l) &&
+	test 4 = "$(cat otherfile-4)" &&
+	git am --abort &&
+	test -z "$(git ls-files -u)" &&
+	test_path_is_missing otherfile-4 &&
+	test_path_is_missing file-1 &&
+	test_path_is_missing file-2 &&
+	test 0 -eq $(git log --oneline 2>/dev/null | wc -l) &&
+	test refs/heads/orphan = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success 'am --abort on unborn branch will keep local commits intact' '
+	git checkout -f --orphan orphan &&
+	git reset &&
+	test_must_fail git am 0004-*.patch &&
+	test_commit unrelated2 &&
+	git rev-parse HEAD >expect &&
+	git am --abort &&
+	git rev-parse HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 0794d33..023d7c6 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -218,6 +218,7 @@
 '
 
 test_expect_success 'garbage report in count-objects -v' '
+	test_when_finished "rm -f .git/objects/pack/fake*" &&
 	: >.git/objects/pack/foo &&
 	: >.git/objects/pack/foo.bar &&
 	: >.git/objects/pack/foo.keep &&
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 6003490..d446706 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -53,6 +53,12 @@
 		test_cmp expect actual
 	'
 
+	test_expect_success "counting commits with limiting ($state)" '
+		git rev-list --count HEAD -- 1.t >expect &&
+		git rev-list --use-bitmap-index --count HEAD -- 1.t >actual &&
+		test_cmp expect actual
+	'
+
 	test_expect_success "enumerate --objects ($state)" '
 		git rev-list --objects --use-bitmap-index HEAD >tmp &&
 		cut -d" " -f1 <tmp >tmp2 &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 8a5f236..ec22c98 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1120,6 +1120,61 @@
 	)
 '
 
+for configallowtipsha1inwant in true false
+do
+	test_expect_success "shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant" '
+		mk_empty testrepo &&
+		(
+			cd testrepo &&
+			git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+			git commit --allow-empty -m foo &&
+			git commit --allow-empty -m bar
+		) &&
+		SHA1=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+		mk_empty shallow &&
+		(
+			cd shallow &&
+			test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
+			git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+			git fetch --depth=1 ../testrepo/.git $SHA1 &&
+			git cat-file commit $SHA1
+		)
+	'
+
+	test_expect_success "deny fetch unreachable SHA1, allowtipsha1inwant=$configallowtipsha1inwant" '
+		mk_empty testrepo &&
+		(
+			cd testrepo &&
+			git config uploadpack.allowtipsha1inwant $configallowtipsha1inwant &&
+			git commit --allow-empty -m foo &&
+			git commit --allow-empty -m bar &&
+			git commit --allow-empty -m xyz
+		) &&
+		SHA1_1=$(git --git-dir=testrepo/.git rev-parse HEAD^^) &&
+		SHA1_2=$(git --git-dir=testrepo/.git rev-parse HEAD^) &&
+		SHA1_3=$(git --git-dir=testrepo/.git rev-parse HEAD) &&
+		(
+			cd testrepo &&
+			git reset --hard $SHA1_2 &&
+			git cat-file commit $SHA1_1 &&
+			git cat-file commit $SHA1_3
+		) &&
+		mk_empty shallow &&
+		(
+			cd shallow &&
+			test_must_fail git fetch ../testrepo/.git $SHA1_3 &&
+			test_must_fail git fetch ../testrepo/.git $SHA1_1 &&
+			git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
+			git fetch ../testrepo/.git $SHA1_1 &&
+			git cat-file commit $SHA1_1 &&
+			test_must_fail git cat-file commit $SHA1_2 &&
+			git fetch ../testrepo/.git $SHA1_2 &&
+			git cat-file commit $SHA1_2 &&
+			test_must_fail git fetch ../testrepo/.git $SHA1_3
+		)
+	'
+done
+
 test_expect_success 'fetch follows tags by default' '
 	mk_test testrepo heads/master &&
 	rm -fr src dst &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 227d293..f4a7193 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -9,36 +9,27 @@
 	mv "$2.x" "$2"
 }
 
-D=`pwd`
-
 test_expect_success setup '
-
 	echo file >file &&
 	git add file &&
 	git commit -a -m original
-
 '
 
 test_expect_success 'pulling into void' '
-	mkdir cloned &&
-	cd cloned &&
-	git init &&
-	git pull ..
-'
-
-cd "$D"
-
-test_expect_success 'checking the results' '
+	git init cloned &&
+	(
+		cd cloned &&
+		git pull ..
+	) &&
 	test -f file &&
 	test -f cloned/file &&
 	test_cmp file cloned/file
 '
 
 test_expect_success 'pulling into void using master:master' '
-	mkdir cloned-uho &&
+	git init cloned-uho &&
 	(
 		cd cloned-uho &&
-		git init &&
 		git pull .. master:master
 	) &&
 	test -f file &&
@@ -71,7 +62,6 @@
 	)
 '
 
-
 test_expect_success 'pulling into void does not remove new staged files' '
 	git init cloned-staged-new &&
 	(
@@ -86,17 +76,29 @@
 	)
 '
 
-test_expect_success 'test . as a remote' '
+test_expect_success 'pulling into void must not create an octopus' '
+	git init cloned-octopus &&
+	(
+		cd cloned-octopus &&
+		test_must_fail git pull .. master master &&
+		! test -f file
+	)
+'
 
+test_expect_success 'test . as a remote' '
 	git branch copy master &&
 	git config branch.copy.remote . &&
 	git config branch.copy.merge refs/heads/master &&
 	echo updated >file &&
 	git commit -a -m updated &&
 	git checkout copy &&
-	test `cat file` = file &&
+	test "$(cat file)" = file &&
 	git pull &&
-	test `cat file` = updated
+	test "$(cat file)" = updated &&
+	git reflog -1 >reflog.actual &&
+	sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+	echo "OBJID HEAD@{0}: pull: Fast-forward" >reflog.expected &&
+	test_cmp reflog.expected reflog.fuzzy
 '
 
 test_expect_success 'the default remote . should not break explicit pull' '
@@ -105,9 +107,120 @@
 	git commit -a -m modified &&
 	git checkout copy &&
 	git reset --hard HEAD^ &&
-	test `cat file` = file &&
+	test "$(cat file)" = file &&
 	git pull . second &&
-	test `cat file` = modified
+	test "$(cat file)" = modified &&
+	git reflog -1 >reflog.actual &&
+	sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+	echo "OBJID HEAD@{0}: pull . second: Fast-forward" >reflog.expected &&
+	test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success 'fail if wildcard spec does not match any refs' '
+	git checkout -b test copy^ &&
+	test_when_finished "git checkout -f copy && git branch -D test" &&
+	test "$(cat file)" = file &&
+	test_must_fail git pull . "refs/nonexisting1/*:refs/nonexisting2/*" 2>err &&
+	test_i18ngrep "no candidates for merging" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'fail if no branches specified with non-default remote' '
+	git remote add test_remote . &&
+	test_when_finished "git remote remove test_remote" &&
+	git checkout -b test copy^ &&
+	test_when_finished "git checkout -f copy && git branch -D test" &&
+	test "$(cat file)" = file &&
+	test_config branch.test.remote origin &&
+	test_must_fail git pull test_remote 2>err &&
+	test_i18ngrep "specify a branch on the command line" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'fail if not on a branch' '
+	git remote add origin . &&
+	test_when_finished "git remote remove origin" &&
+	git checkout HEAD^ &&
+	test_when_finished "git checkout -f copy" &&
+	test "$(cat file)" = file &&
+	test_must_fail git pull 2>err &&
+	test_i18ngrep "not currently on a branch" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'fail if no configuration for current branch' '
+	git remote add test_remote . &&
+	test_when_finished "git remote remove test_remote" &&
+	git checkout -b test copy^ &&
+	test_when_finished "git checkout -f copy && git branch -D test" &&
+	test_config branch.test.remote test_remote &&
+	test "$(cat file)" = file &&
+	test_must_fail git pull 2>err &&
+	test_i18ngrep "no tracking information" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'pull --all: fail if no configuration for current branch' '
+	git remote add test_remote . &&
+	test_when_finished "git remote remove test_remote" &&
+	git checkout -b test copy^ &&
+	test_when_finished "git checkout -f copy && git branch -D test" &&
+	test_config branch.test.remote test_remote &&
+	test "$(cat file)" = file &&
+	test_must_fail git pull --all 2>err &&
+	test_i18ngrep "There is no tracking information" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'fail if upstream branch does not exist' '
+	git checkout -b test copy^ &&
+	test_when_finished "git checkout -f copy && git branch -D test" &&
+	test_config branch.test.remote . &&
+	test_config branch.test.merge refs/heads/nonexisting &&
+	test "$(cat file)" = file &&
+	test_must_fail git pull 2>err &&
+	test_i18ngrep "no such ref was fetched" err &&
+	test "$(cat file)" = file
+'
+
+test_expect_success 'fail if the index has unresolved entries' '
+	git checkout -b third second^ &&
+	test_when_finished "git checkout -f copy && git branch -D third" &&
+	test "$(cat file)" = file &&
+	test_commit modified2 file &&
+	test -z "$(git ls-files -u)" &&
+	test_must_fail git pull . second &&
+	test -n "$(git ls-files -u)" &&
+	cp file expected &&
+	test_must_fail git pull . second 2>err &&
+	test_i18ngrep "Pull is not possible because you have unmerged files" err &&
+	test_cmp expected file &&
+	git add file &&
+	test -z "$(git ls-files -u)" &&
+	test_must_fail git pull . second 2>err &&
+	test_i18ngrep "You have not concluded your merge" err &&
+	test_cmp expected file
+'
+
+test_expect_success 'fast-forwards working tree if branch head is updated' '
+	git checkout -b third second^ &&
+	test_when_finished "git checkout -f copy && git branch -D third" &&
+	test "$(cat file)" = file &&
+	git pull . second:third 2>err &&
+	test_i18ngrep "fetch updated the current branch head" err &&
+	test "$(cat file)" = modified &&
+	test "$(git rev-parse third)" = "$(git rev-parse second)"
+'
+
+test_expect_success 'fast-forward fails with conflicting work tree' '
+	git checkout -b third second^ &&
+	test_when_finished "git checkout -f copy && git branch -D third" &&
+	test "$(cat file)" = file &&
+	echo conflict >file &&
+	test_must_fail git pull . second:third 2>err &&
+	test_i18ngrep "Cannot fast-forward your working tree" err &&
+	test "$(cat file)" = conflict &&
+	test "$(git rev-parse third)" = "$(git rev-parse second)"
 '
 
 test_expect_success '--rebase' '
@@ -120,23 +233,32 @@
 	git commit -m "new file" &&
 	git tag before-rebase &&
 	git pull --rebase . copy &&
-	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
-	test new = $(git show HEAD:file2)
+	test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+	test new = "$(git show HEAD:file2)"
 '
+
+test_expect_success '--rebase fails with multiple branches' '
+	git reset --hard before-rebase &&
+	test_must_fail git pull --rebase . copy master 2>err &&
+	test "$(git rev-parse HEAD)" = "$(git rev-parse before-rebase)" &&
+	test_i18ngrep "Cannot rebase onto multiple branches" err &&
+	test modified = "$(git show HEAD:file)"
+'
+
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
 	test_config pull.rebase true &&
 	git pull . copy &&
-	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
-	test new = $(git show HEAD:file2)
+	test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+	test new = "$(git show HEAD:file2)"
 '
 
 test_expect_success 'branch.to-rebase.rebase' '
 	git reset --hard before-rebase &&
 	test_config branch.to-rebase.rebase true &&
 	git pull . copy &&
-	test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
-	test new = $(git show HEAD:file2)
+	test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" &&
+	test new = "$(git show HEAD:file2)"
 '
 
 test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
@@ -144,8 +266,8 @@
 	test_config pull.rebase true &&
 	test_config branch.to-rebase.rebase false &&
 	git pull . copy &&
-	test $(git rev-parse HEAD^) != $(git rev-parse copy) &&
-	test new = $(git show HEAD:file2)
+	test "$(git rev-parse HEAD^)" != "$(git rev-parse copy)" &&
+	test new = "$(git show HEAD:file2)"
 '
 
 # add a feature branch, keep-merge, that is merged into master, so the
@@ -164,33 +286,33 @@
 	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 "$(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 "$(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 "$(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 "$(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' '
@@ -203,25 +325,25 @@
 	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 "$(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 "$(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 "$(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' '
@@ -233,8 +355,8 @@
 	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 "$(git rev-parse HEAD^^)" = "$(git rev-parse copy)" &&
+	test file3 = "$(git show HEAD:file3.t)"
 '
 
 test_expect_success '--rebase with rebased upstream' '
@@ -251,10 +373,18 @@
 	git tag to-rebase-orig &&
 	git pull --rebase me copy &&
 	test "conflicting modification" = "$(cat file)" &&
-	test file = $(cat file2)
+	test file = "$(cat file2)"
 
 '
 
+test_expect_success '--rebase -f with rebased upstream' '
+	test_when_finished "test_might_fail git rebase --abort" &&
+	git reset --hard to-rebase-orig &&
+	git pull --rebase -f me copy &&
+	test "conflicting modification" = "$(cat file)" &&
+	test file = "$(cat file2)"
+'
+
 test_expect_success '--rebase with rebased default upstream' '
 
 	git update-ref refs/remotes/me/copy copy-orig &&
@@ -262,7 +392,7 @@
 	git reset --hard to-rebase-orig &&
 	git pull --rebase &&
 	test "conflicting modification" = "$(cat file)" &&
-	test file = $(cat file2)
+	test file = "$(cat file2)"
 
 '
 
@@ -283,7 +413,7 @@
 
 	git checkout to-rebase &&
 	git update-ref refs/remotes/me/copy copy^ &&
-	COPY=$(git rev-parse --verify me/copy) &&
+	COPY="$(git rev-parse --verify me/copy)" &&
 	git rebase --onto $COPY copy &&
 	test_config branch.to-rebase.remote me &&
 	test_config branch.to-rebase.merge refs/heads/copy &&
@@ -291,10 +421,10 @@
 	echo dirty >> file &&
 	git add file &&
 	test_must_fail git pull &&
-	test $COPY = $(git rev-parse --verify me/copy) &&
+	test "$COPY" = "$(git rev-parse --verify me/copy)" &&
 	git checkout HEAD -- file &&
 	git pull &&
-	test $COPY != $(git rev-parse --verify me/copy)
+	test "$COPY" != "$(git rev-parse --verify me/copy)"
 
 '
 
@@ -309,6 +439,21 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
+	test_when_finished "rm -rf empty_repo2" &&
+	git init empty_repo2 &&
+	(
+		cd empty_repo2 &&
+		echo staged-file >staged-file &&
+		git add staged-file &&
+		test "$(git ls-files)" = staged-file &&
+		test_must_fail git pull --rebase .. master 2>err &&
+		test "$(git ls-files)" = staged-file &&
+		test "$(git show :staged-file)" = staged-file &&
+		test_i18ngrep "unborn branch with changes added to the index" err
+	)
+'
+
 test_expect_success 'setup for detecting upstreamed changes' '
 	mkdir src &&
 	(cd src &&
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 453aba5..18372ca 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -117,4 +117,31 @@
 	)
 '
 
+test_expect_success 'git pull --dry-run' '
+	test_when_finished "rm -rf clonedry" &&
+	git init clonedry &&
+	(
+		cd clonedry &&
+		git pull --dry-run ../parent &&
+		test_path_is_missing .git/FETCH_HEAD &&
+		test_path_is_missing .git/refs/heads/master &&
+		test_path_is_missing .git/index &&
+		test_path_is_missing file
+	)
+'
+
+test_expect_success 'git pull --all --dry-run' '
+	test_when_finished "rm -rf cloneddry" &&
+	git init clonedry &&
+	(
+		cd clonedry &&
+		git remote add origin ../parent &&
+		git pull --all --dry-run &&
+		test_path_is_missing .git/FETCH_HEAD &&
+		test_path_is_missing .git/refs/remotes/origin/master &&
+		test_path_is_missing .git/index &&
+		test_path_is_missing file
+	)
+'
+
 test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index 27c3d73..2af1bee 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -24,7 +24,7 @@
 '
 
 test_expect_success 'Merge with d/f conflicts' '
-	test_expect_code 1 git merge "merge msg" B master
+	test_expect_code 1 git merge -m "merge msg" master
 '
 
 test_expect_success 'F/D conflict' '
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index d15b313..213deec 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -48,7 +48,7 @@
 " > file &&
 git commit -m "C3" file &&
 git branch C3 &&
-git merge "pre E3 merge" B A &&
+git merge -m "pre E3 merge" A &&
 echo "1
 2
 3 changed in E3, branch B. New file size
@@ -61,7 +61,7 @@
 " > file &&
 git commit -m "E3" file &&
 git checkout A &&
-git merge "pre D8 merge" A C3 &&
+git merge -m "pre D8 merge" C3 &&
 echo "1
 2
 3 changed in C3, branch B
@@ -73,7 +73,7 @@
 9" > file &&
 git commit -m D8 file'
 
-test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+test_expect_success 'Criss-cross merge' 'git merge -m "final merge" B'
 
 cat > file-expect <<EOF
 1
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
index 3c21938..04c0509 100755
--- a/t/t6026-merge-attr.sh
+++ b/t/t6026-merge-attr.sh
@@ -85,11 +85,12 @@
 cat >./custom-merge <<\EOF
 #!/bin/sh
 
-orig="$1" ours="$2" theirs="$3" exit="$4"
+orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
 (
 	echo "orig is $orig"
 	echo "ours is $ours"
 	echo "theirs is $theirs"
+	echo "path is $path"
 	echo "=== orig ==="
 	cat "$orig"
 	echo "=== ours ==="
@@ -110,7 +111,7 @@
 
 	git reset --hard anchor &&
 	git config --replace-all \
-	merge.custom.driver "./custom-merge %O %A %B 0" &&
+	merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
@@ -121,7 +122,7 @@
 	o=$(git unpack-file master^:text) &&
 	a=$(git unpack-file side^:text) &&
 	b=$(git unpack-file master:text) &&
-	sh -c "./custom-merge $o $a $b 0" &&
+	sh -c "./custom-merge $o $a $b 0 'text'" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
 	rm -f $o $a $b
@@ -131,7 +132,7 @@
 
 	git reset --hard anchor &&
 	git config --replace-all \
-	merge.custom.driver "./custom-merge %O %A %B 1" &&
+	merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
@@ -148,9 +149,12 @@
 	o=$(git unpack-file master^:text) &&
 	a=$(git unpack-file anchor:text) &&
 	b=$(git unpack-file master:text) &&
-	sh -c "./custom-merge $o $a $b 0" &&
+	sh -c "./custom-merge $o $a $b 0 'text'" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
+	sed -e 1,3d -e 4q $a >check-3 &&
+	echo "path is text" >expect &&
+	cmp expect check-3 &&
 	rm -f $o $a $b
 '
 
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index c66bf79..24fc2ba 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -28,7 +28,10 @@
 	git update-ref refs/remotes/origin/master master &&
 	git remote add origin nowhere &&
 	git config branch.master.remote origin &&
-	git config branch.master.merge refs/heads/master
+	git config branch.master.merge refs/heads/master &&
+	git remote add myfork elsewhere &&
+	git config remote.pushdefault myfork &&
+	git config push.default current
 '
 
 test_atom() {
@@ -47,6 +50,7 @@
 
 test_atom head refname refs/heads/master
 test_atom head upstream refs/remotes/origin/master
+test_atom head push refs/remotes/myfork/master
 test_atom head objecttype commit
 test_atom head objectsize 171
 test_atom head objectname $(git rev-parse refs/heads/master)
@@ -83,6 +87,7 @@
 
 test_atom tag refname refs/tags/testtag
 test_atom tag upstream ''
+test_atom tag push ''
 test_atom tag objecttype tag
 test_atom tag objectsize 154
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
@@ -347,6 +352,12 @@
 	test_cmp expected actual
 '
 
+test_expect_success '%(push) supports tracking specifiers, too' '
+	echo "[ahead 1]" >expected &&
+	git for-each-ref --format="%(push:track)" refs/heads >actual &&
+	test_cmp expected actual
+'
+
 cat >expected <<EOF
 $(git rev-parse --short HEAD)
 EOF
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
new file mode 100755
index 0000000..cdb67a0
--- /dev/null
+++ b/t/t6301-for-each-ref-errors.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='for-each-ref errors for broken refs'
+
+. ./test-lib.sh
+
+ZEROS=$_z40
+MISSING=abababababababababababababababababababab
+
+test_expect_success setup '
+	git commit --allow-empty -m "Initial" &&
+	git tag testtag &&
+	git for-each-ref >full-list &&
+	git for-each-ref --format="%(objectname) %(refname)" >brief-list
+'
+
+test_expect_success 'Broken refs are reported correctly' '
+	r=refs/heads/bogus &&
+	: >.git/$r &&
+	test_when_finished "rm -f .git/$r" &&
+	echo "warning: ignoring broken ref $r" >broken-err &&
+	git for-each-ref >out 2>err &&
+	test_cmp full-list out &&
+	test_cmp broken-err err
+'
+
+test_expect_success 'NULL_SHA1 refs are reported correctly' '
+	r=refs/heads/zeros &&
+	echo $ZEROS >.git/$r &&
+	test_when_finished "rm -f .git/$r" &&
+	echo "warning: ignoring broken ref $r" >zeros-err &&
+	git for-each-ref >out 2>err &&
+	test_cmp full-list out &&
+	test_cmp zeros-err err &&
+	git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
+	test_cmp brief-list brief-out &&
+	test_cmp zeros-err brief-err
+'
+
+test_expect_success 'Missing objects are reported correctly' '
+	r=refs/heads/missing &&
+	echo $MISSING >.git/$r &&
+	test_when_finished "rm -f .git/$r" &&
+	echo "fatal: missing object $MISSING for $r" >missing-err &&
+	test_must_fail git for-each-ref 2>err &&
+	test_cmp missing-err err &&
+	(
+		cat brief-list &&
+		echo "$MISSING $r"
+	) | sort -k 2 >missing-brief-expected &&
+	git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
+	test_cmp missing-brief-expected brief-out &&
+	test_must_be_empty brief-err
+'
+
+test_done
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
new file mode 100755
index 0000000..bd4806c
--- /dev/null
+++ b/t/t7063-status-untracked-cache.sh
@@ -0,0 +1,357 @@
+#!/bin/sh
+
+test_description='test untracked cache'
+
+. ./test-lib.sh
+
+avoid_racy() {
+	sleep 1
+}
+
+# It's fine if git update-index returns an error code other than one,
+# it'll be caught in the first test.
+test_lazy_prereq UNTRACKED_CACHE '
+	{ git update-index --untracked-cache; ret=$?; } &&
+	test $ret -ne 1
+'
+
+if ! test_have_prereq UNTRACKED_CACHE; then
+	skip_all='This system does not support untracked cache'
+	test_done
+fi
+
+test_expect_success 'setup' '
+	git init worktree &&
+	cd worktree &&
+	mkdir done dtwo dthree &&
+	touch one two three done/one dtwo/two dthree/three &&
+	git add one two done/one &&
+	: >.git/info/exclude &&
+	git update-index --untracked-cache
+'
+
+test_expect_success 'untracked cache is empty' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 0000000000000000000000000000000000000000
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+EOF
+	test_cmp ../expect ../actual
+'
+
+cat >../status.expect <<EOF &&
+A  done/one
+A  one
+A  two
+?? dthree/
+?? dtwo/
+?? three
+EOF
+
+cat >../dump.expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ 0000000000000000000000000000000000000000 recurse valid
+dthree/
+dtwo/
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+
+test_expect_success 'status first time (empty cache)' '
+	avoid_racy &&
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 3
+gitignore invalidation: 1
+directory invalidation: 0
+opendir: 4
+EOF
+	test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after first status' '
+	test-dump-untracked-cache >../actual &&
+	test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'status second time (fully populated cache)' '
+	avoid_racy &&
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 0
+EOF
+	test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after second status' '
+	test-dump-untracked-cache >../actual &&
+	test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'modify in root directory, one dir invalidation' '
+	avoid_racy &&
+	: >four &&
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	cat >../status.expect <<EOF &&
+A  done/one
+A  one
+A  two
+?? dthree/
+?? dtwo/
+?? four
+?? three
+EOF
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 1
+opendir: 1
+EOF
+	test_cmp ../trace.expect ../trace
+
+'
+
+test_expect_success 'verify untracked cache dump' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ 0000000000000000000000000000000000000000 recurse valid
+dthree/
+dtwo/
+four
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'new .gitignore invalidates recursively' '
+	avoid_racy &&
+	echo four >.gitignore &&
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	cat >../status.expect <<EOF &&
+A  done/one
+A  one
+A  two
+?? .gitignore
+?? dthree/
+?? dtwo/
+?? three
+EOF
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 1
+directory invalidation: 1
+opendir: 4
+EOF
+	test_cmp ../trace.expect ../trace
+
+'
+
+test_expect_success 'verify untracked cache dump' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dthree/
+dtwo/
+three
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+three
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'new info/exclude invalidates everything' '
+	avoid_racy &&
+	echo three >>.git/info/exclude &&
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	cat >../status.expect <<EOF &&
+A  done/one
+A  one
+A  two
+?? .gitignore
+?? dtwo/
+EOF
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 1
+directory invalidation: 0
+opendir: 4
+EOF
+	test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'move two from tracked to untracked' '
+	git rm --cached two &&
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'status after the move' '
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	cat >../status.expect <<EOF &&
+A  done/one
+A  one
+?? .gitignore
+?? dtwo/
+?? two
+EOF
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 1
+EOF
+	test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+two
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'move two from untracked to tracked' '
+	git add two &&
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_expect_success 'status after the move' '
+	: >../trace &&
+	GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
+	git status --porcelain >../actual &&
+	cat >../status.expect <<EOF &&
+A  done/one
+A  one
+A  two
+?? .gitignore
+?? dtwo/
+EOF
+	test_cmp ../status.expect ../actual &&
+	cat >../trace.expect <<EOF &&
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 1
+EOF
+	test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'verify untracked cache dump' '
+	test-dump-untracked-cache >../actual &&
+	cat >../expect <<EOF &&
+info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
+core.excludesfile 0000000000000000000000000000000000000000
+exclude_per_dir .gitignore
+flags 00000006
+/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
+.gitignore
+dtwo/
+/done/ 0000000000000000000000000000000000000000 recurse valid
+/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
+/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
+two
+EOF
+	test_cmp ../expect ../actual
+'
+
+test_done
diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh
new file mode 100755
index 0000000..8f30aed
--- /dev/null
+++ b/t/t7410-submodule-checkout-to.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Combination of submodules and multiple workdirs'
+
+. ./test-lib.sh
+
+base_path=$(pwd -P)
+
+test_expect_success 'setup: make origin' \
+    'mkdir -p origin/sub && ( cd origin/sub && git init &&
+	echo file1 >file1 &&
+	git add file1 &&
+	git commit -m file1 ) &&
+    mkdir -p origin/main && ( cd origin/main && git init &&
+	git submodule add ../sub &&
+	git commit -m "add sub" ) &&
+    ( cd origin/sub &&
+	echo file1updated >file1 &&
+	git add file1 &&
+	git commit -m "file1 updated" ) &&
+    ( cd origin/main/sub && git pull ) &&
+    ( cd origin/main &&
+	git add sub &&
+	git commit -m "sub updated" )'
+
+test_expect_success 'setup: clone' \
+    'mkdir clone && ( cd clone &&
+	git clone --recursive "$base_path/origin/main")'
+
+rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1")
+rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1")
+
+test_expect_success 'checkout main' \
+    'mkdir default_checkout &&
+    (cd clone/main &&
+	git checkout --to "$base_path/default_checkout/main" "$rev1_hash_main")'
+
+test_expect_failure 'can see submodule diffs just after checkout' \
+    '(cd default_checkout/main && git diff --submodule master"^!" | grep "file1 updated")'
+
+test_expect_success 'checkout main and initialize independed clones' \
+    'mkdir fully_cloned_submodule &&
+    (cd clone/main &&
+	git checkout --to "$base_path/fully_cloned_submodule/main" "$rev1_hash_main") &&
+    (cd fully_cloned_submodule/main && git submodule update)'
+
+test_expect_success 'can see submodule diffs after independed cloning' \
+    '(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")'
+
+test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 051489e..b39e313 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -229,14 +229,36 @@
 	cat >text <<EOF &&
 
 # to be kept
+
+  # ------------------------ >8 ------------------------
+# to be kept, too
 # ------------------------ >8 ------------------------
 to be removed
+# ------------------------ >8 ------------------------
+to be removed, too
 EOF
-	echo "# to be kept" >expect &&
+
+	cat >expect <<EOF &&
+# to be kept
+
+  # ------------------------ >8 ------------------------
+# to be kept, too
+EOF
 	git commit --cleanup=scissors -e -F text -a &&
 	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
 	test_cmp expect actual
+'
 
+test_expect_success 'cleanup commit messages (scissors option,-F,-e, scissors on first line)' '
+
+	echo >>negative &&
+	cat >text <<EOF &&
+# ------------------------ >8 ------------------------
+to be removed
+EOF
+	git commit --cleanup=scissors -e -F text -a --allow-empty-message &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'cleanup commit messages (strip option,-F)' '
@@ -370,7 +392,7 @@
 EOF
 
 test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
-	>.git/result
+	>.git/result &&
 	>expect &&
 
 	echo >>negative &&
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 5cdf3f1..ff09ace 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -19,4 +19,66 @@
 		"<E at test dot git>" 1
 '
 
+test_expect_success 'setup showEmail tests' '
+	echo "bin: test number 1" >one &&
+	git add one &&
+	GIT_AUTHOR_NAME=name1 \
+	GIT_AUTHOR_EMAIL=email1@test.git \
+	git commit -m First --date="2010-01-01 01:00:00" &&
+	cat >expected_n <<-\EOF &&
+	(name1 2010-01-01 01:00:00 +0000 1) bin: test number 1
+	EOF
+	cat >expected_e <<-\EOF
+	(<email1@test.git> 2010-01-01 01:00:00 +0000 1) bin: test number 1
+	EOF
+'
+
+find_blame () {
+	sed -e 's/^[^(]*//'
+}
+
+test_expect_success 'blame with no options and no config' '
+	git blame one >blame &&
+	find_blame <blame >result &&
+	test_cmp expected_n result
+'
+
+test_expect_success 'blame with showemail options' '
+	git blame --show-email one >blame1 &&
+	find_blame <blame1 >result &&
+	test_cmp expected_e result &&
+	git blame -e one >blame2 &&
+	find_blame <blame2 >result &&
+	test_cmp expected_e result &&
+	git blame --no-show-email one >blame3 &&
+	find_blame <blame3 >result &&
+	test_cmp expected_n result
+'
+
+test_expect_success 'blame with showEmail config false' '
+	git config blame.showEmail false &&
+	git blame one >blame1 &&
+	find_blame <blame1 >result &&
+	test_cmp expected_n result &&
+	git blame --show-email one >blame2 &&
+	find_blame <blame2 >result &&
+	test_cmp expected_e result &&
+	git blame -e one >blame3 &&
+	find_blame <blame3 >result &&
+	test_cmp expected_e result &&
+	git blame --no-show-email one >blame4 &&
+	find_blame <blame4 >result &&
+	test_cmp expected_n result
+'
+
+test_expect_success 'blame with showEmail config true' '
+	git config blame.showEmail true &&
+	git blame one >blame1 &&
+	find_blame <blame1 >result &&
+	test_cmp expected_e result &&
+	git blame --no-show-email one >blame2 &&
+	find_blame <blame2 >result &&
+	test_cmp expected_n result
+'
+
 test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 7be14a4..db2f45e 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1537,7 +1537,7 @@
 
 test_expect_success $PREREQ 'sendemail.aliasfile=~/.mailrc' '
 	clean_fake_sendmail &&
-	echo "alias sbd  someone@example.org" >~/.mailrc &&
+	echo "alias sbd  someone@example.org" >"$HOME/.mailrc" &&
 	git config --replace-all sendemail.aliasesfile "~/.mailrc" &&
 	git config sendemail.aliasfiletype mailrc &&
 	git send-email \
@@ -1549,6 +1549,78 @@
 	grep "^!someone@example\.org!$" commandline1
 '
 
+test_sendmail_aliases () {
+	msg="$1" && shift &&
+	expect="$@" &&
+	cat >.tmp-email-aliases &&
+
+	test_expect_success $PREREQ "$msg" '
+		clean_fake_sendmail && rm -fr outdir &&
+		git format-patch -1 -o outdir &&
+		git config --replace-all sendemail.aliasesfile \
+			"$(pwd)/.tmp-email-aliases" &&
+		git config sendemail.aliasfiletype sendmail &&
+		git send-email \
+			--from="Example <nobody@example.com>" \
+			--to=alice --to=bcgrp \
+			--smtp-server="$(pwd)/fake.sendmail" \
+			outdir/0001-*.patch \
+			2>errors >out &&
+		for i in $expect
+		do
+			grep "^!$i!$" commandline1 || return 1
+		done
+	'
+}
+
+test_sendmail_aliases 'sendemail.aliasfiletype=sendmail' \
+	'awol@example\.com' \
+	'bob@example\.com' \
+	'chloe@example\.com' \
+	'o@example\.com' <<-\EOF
+	alice: Alice W Land <awol@example.com>
+	bob: Robert Bobbyton <bob@example.com>
+	# this is a comment
+	   # this is also a comment
+	chloe: chloe@example.com
+	abgroup: alice, bob
+	bcgrp: bob, chloe, Other <o@example.com>
+	EOF
+
+test_sendmail_aliases 'sendmail aliases line folding' \
+	alice1 \
+	bob1 bob2 \
+	chuck1 chuck2 \
+	darla1 darla2 darla3 \
+	elton1 elton2 elton3 \
+	fred1 fred2 \
+	greg1 <<-\EOF
+	alice: alice1
+	bob: bob1,\
+	bob2
+	chuck: chuck1,
+	    chuck2
+	darla: darla1,\
+	darla2,
+	    darla3
+	elton: elton1,
+	    elton2,\
+	elton3
+	fred: fred1,\
+	    fred2
+	greg: greg1
+	bcgrp: bob, chuck, darla, elton, fred, greg
+	EOF
+
+test_sendmail_aliases 'sendmail aliases tolerate bogus line folding' \
+	alice1 bob1 <<-\EOF
+	    alice: alice1
+	bcgrp: bob1\
+	EOF
+
+test_sendmail_aliases 'sendmail aliases empty' alice bcgrp <<-\EOF
+	EOF
+
 do_xmailer_test () {
 	expected=$1 params=$2 &&
 	git format-patch -1 &&
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 1e266ef..d00df08 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -496,7 +496,7 @@
 '
 
 test_expect_success 'merge early [cvswork3] b3 with b1' '
-	( cd gitwork3 && git merge "message" HEAD b1 ) &&
+	( cd gitwork3 && git merge -m "message" b1 ) &&
 	git fetch gitwork3 b3:b3 &&
 	git tag v3merged b3 &&
 	git push --tags gitcvs.git b3:b3
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index 5b56212..90d41ed 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -131,6 +131,44 @@
 	)
 '
 
+revision_ranges="2000/01/01,#head \
+		 1,2080/01/01 \
+		 2000/01/01,2080/01/01 \
+		 2000/01/01,1000 \
+		 1,1000"
+
+test_expect_success 'clone using non-numeric revision ranges' '
+	test_when_finished cleanup_git &&
+	for r in $revision_ranges
+	do
+		rm -fr "$git" &&
+		test ! -d "$git" &&
+		git p4 clone --dest="$git" //depot@$r &&
+		(
+			cd "$git" &&
+			git ls-files >lines &&
+			test_line_count = 6 lines
+		)
+	done
+'
+
+test_expect_success 'clone with date range, excluding some changes' '
+	test_when_finished cleanup_git &&
+	before=$(date +%Y/%m/%d:%H:%M:%S) &&
+	sleep 2 &&
+	(
+		cd "$cli" &&
+		:>date_range_test &&
+		p4 add date_range_test &&
+		p4 submit -d "Adding file"
+	) &&
+	git p4 clone --dest="$git" //depot@1,$before &&
+	(
+		cd "$git" &&
+		test_path_is_missing date_range_test
+	)
+'
+
 test_expect_success 'exit when p4 fails to produce marshaled output' '
 	mkdir badp4dir &&
 	test_when_finished "rm badp4dir/p4 && rmdir badp4dir" &&
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 2bf142d..0aafd03 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -504,6 +504,112 @@
 	)
 '
 
+test_expect_success 'restart p4d' '
+	kill_p4d &&
+	start_p4d
+'
+
+#
+# 1: //depot/branch1/base/file1
+#    //depot/branch1/base/file2
+#    //depot/branch1/base/dir/sub_file1
+# 2: integrate //depot/branch1/base/... -> //depot/branch2/base/...
+# 3: //depot/branch1/base/file3
+# 4: //depot/branch1/base/file2 (edit)
+# 5: integrate //depot/branch1/base/... -> //depot/branch3/base/...
+#
+# Note: the client view removes the "base" folder from the workspace
+#       and moves sub_file1 one level up.
+test_expect_success 'add simple p4 branches with common base folder on each branch' '
+	(
+		cd "$cli" &&
+		client_view "//depot/branch1/base/... //client/branch1/..." \
+			    "//depot/branch1/base/dir/sub_file1 //client/branch1/sub_file1" \
+			    "//depot/branch2/base/... //client/branch2/..." \
+			    "//depot/branch3/base/... //client/branch3/..." &&
+		mkdir -p branch1 &&
+		cd branch1 &&
+		echo file1 >file1 &&
+		echo file2 >file2 &&
+		mkdir dir &&
+		echo sub_file1 >sub_file1 &&
+		p4 add file1 file2 sub_file1 &&
+		p4 submit -d "Create branch1" &&
+		p4 integrate //depot/branch1/base/... //depot/branch2/base/... &&
+		p4 submit -d "Integrate branch2 from branch1" &&
+		echo file3 >file3 &&
+		p4 add file3 &&
+		p4 submit -d "add file3 in branch1" &&
+		p4 open file2 &&
+		echo update >>file2 &&
+		p4 submit -d "update file2 in branch1" &&
+		p4 integrate //depot/branch1/base/... //depot/branch3/base/... &&
+		p4 submit -d "Integrate branch3 from branch1"
+	)
+'
+
+# Configure branches through git-config and clone them.
+# All files are tested to make sure branches were cloned correctly.
+# Finally, make an update to branch1 on P4 side to check if it is imported
+# correctly by git p4.
+# git p4 is expected to use the client view to also not include the common
+# "base" folder in the imported directory structure.
+test_expect_success 'git p4 clone simple branches with base folder on server side' '
+	test_create_repo "$git" &&
+	(
+		cd "$git" &&
+		git config git-p4.branchList branch1:branch2 &&
+		git config --add git-p4.branchList branch1:branch3 &&
+		git p4 clone --dest=. --use-client-spec  --detect-branches //depot@all &&
+		git log --all --graph --decorate --stat &&
+		git reset --hard p4/depot/branch1 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		test -f sub_file1 &&
+		grep update file2 &&
+		git reset --hard p4/depot/branch2 &&
+		test -f file1 &&
+		test -f file2 &&
+		test ! -f file3 &&
+		test -f sub_file1 &&
+		! grep update file2 &&
+		git reset --hard p4/depot/branch3 &&
+		test -f file1 &&
+		test -f file2 &&
+		test -f file3 &&
+		test -f sub_file1 &&
+		grep update file2 &&
+		cd "$cli" &&
+		cd branch1 &&
+		p4 edit file2 &&
+		echo file2_ >>file2 &&
+		p4 submit -d "update file2 in branch1" &&
+		cd "$git" &&
+		git reset --hard p4/depot/branch1 &&
+		git p4 rebase &&
+		grep file2_ file2
+	)
+'
+
+# Now update a file in one of the branches in git and submit to P4
+test_expect_success 'Update a file in git side and submit to P4 using client view' '
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git reset --hard p4/depot/branch1 &&
+		echo "client spec" >> file1 &&
+		git add -u . &&
+		git commit -m "update file1 in branch1" &&
+		git config git-p4.skipSubmitEdit true &&
+		git p4 submit --verbose &&
+		cd "$cli" &&
+		p4 sync ... &&
+		cd branch1 &&
+		grep "client spec" file1
+	)
+'
+
 test_expect_success 'kill p4d' '
 	kill_p4d
 '
diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh
index fbacff3..d950c7d 100755
--- a/t/t9803-git-p4-shell-metachars.sh
+++ b/t/t9803-git-p4-shell-metachars.sh
@@ -28,7 +28,7 @@
 		echo f2 >"file with spaces" &&
 		git add "file with spaces" &&
 		git commit -m "add files" &&
-		P4EDITOR=touch git p4 submit
+		P4EDITOR="test-chmtime +5" git p4 submit
 	) &&
 	(
 		cd "$cli" &&
@@ -47,7 +47,7 @@
 		git rm foo\$bar &&
 		git rm file\ with\ spaces &&
 		git commit -m "remove files" &&
-		P4EDITOR=touch git p4 submit
+		P4EDITOR="test-chmtime +5" git p4 submit
 	) &&
 	(
 		cd "$cli" &&
diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh
index 8931188..5fbf904 100755
--- a/t/t9805-git-p4-skip-submit-edit.sh
+++ b/t/t9805-git-p4-skip-submit-edit.sh
@@ -90,7 +90,7 @@
 		cd "$git" &&
 		echo line >>file1 &&
 		git commit -a -m "change 5" &&
-		P4EDITOR="$TRASH_DIRECTORY/ed.sh" &&
+		P4EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" &&
 		export P4EDITOR &&
 		git p4 submit &&
 		p4 changes //depot/... >wc &&
diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh
index 166b840..0fe2312 100755
--- a/t/t9813-git-p4-preserve-users.sh
+++ b/t/t9813-git-p4-preserve-users.sh
@@ -53,7 +53,9 @@
 		git commit --author "Alice <alice@example.com>" -m "a change by alice" file1 &&
 		git commit --author "Bob <bob@example.com>" -m "a change by bob" file2 &&
 		git config git-p4.skipSubmitEditCheck true &&
-		P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user &&
+		P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
+		export P4EDITOR P4USER P4PASSWD &&
+		git p4 commit --preserve-user &&
 		p4_check_commit_author file1 alice &&
 		p4_check_commit_author file2 bob
 	)
@@ -69,7 +71,7 @@
 		git config git-p4.skipSubmitEditCheck true &&
 		echo "username-noperms: a change by alice" >>file1 &&
 		git commit --author "Alice <alice@example.com>" -m "perms: a change by alice" file1 &&
-		P4EDITOR=touch P4USER=bob P4PASSWD=secret &&
+		P4EDITOR="test-chmtime +5" P4USER=bob P4PASSWD=secret &&
 		export P4EDITOR P4USER P4PASSWD &&
 		test_must_fail git p4 commit --preserve-user &&
 		! git diff --exit-code HEAD..p4/master
@@ -87,7 +89,7 @@
 		git commit --author "Bob <bob@example.com>" -m "preserve: a change by bob" file1 &&
 		echo "username-unknown: a change by charlie" >>file1 &&
 		git commit --author "Charlie <charlie@example.com>" -m "preserve: a change by charlie" file1 &&
-		P4EDITOR=touch P4USER=alice P4PASSWD=secret &&
+		P4EDITOR="test-chmtime +5" P4USER=alice P4PASSWD=secret &&
 		export P4EDITOR P4USER P4PASSWD &&
 		test_must_fail git p4 commit --preserve-user &&
 		! git diff --exit-code HEAD..p4/master &&
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index 99bb71b..c89992c 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -226,14 +226,9 @@
 
 # See if configurables can be set, and in particular if the run.move.allow
 # variable exists, which allows admins to disable the "p4 move" command.
-test_expect_success 'p4 configure command and run.move.allow are available' '
-	p4 configure show run.move.allow >out ; retval=$? &&
-	test $retval = 0 &&
-	{
-		egrep ^run.move.allow: out &&
-		test_set_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW ||
-		true
-	} || true
+test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
+	p4 configure show run.move.allow >out &&
+	egrep ^run.move.allow: out
 '
 
 # If move can be disabled, turn it off and test p4 move handling
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
index e71e543..d048bd3 100755
--- a/t/t9816-git-p4-locked.sh
+++ b/t/t9816-git-p4-locked.sh
@@ -35,13 +35,13 @@
 	)
 '
 
-test_expect_failure 'add with lock not taken' '
+test_expect_success 'add with lock not taken' '
 	test_when_finished cleanup_git &&
 	git p4 clone --dest="$git" //depot &&
 	(
 		cd "$git" &&
 		echo line1 >>add-lock-not-taken &&
-		git add file2 &&
+		git add add-lock-not-taken &&
 		git commit -m "add add-lock-not-taken" &&
 		git config git-p4.skipSubmitEdit true &&
 		git p4 submit --verbose
@@ -107,7 +107,7 @@
 	)
 '
 
-test_expect_failure 'copy with lock taken' '
+test_expect_success 'copy with lock taken' '
 	lock_in_another_client &&
 	test_when_finished cleanup_git &&
 	test_when_finished "cd \"$cli\" && p4 revert file2 && rm -f file2" &&
@@ -130,8 +130,8 @@
 	git p4 clone --dest="$git" //depot &&
 	(
 		cd "$git" &&
-		git mv file1 file2 &&
-		git commit -m "mv file1 to file2" &&
+		git mv file1 file3 &&
+		git commit -m "mv file1 to file3" &&
 		git config git-p4.skipSubmitEdit true &&
 		git config git-p4.detectRenames true &&
 		git p4 submit --verbose
diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh
new file mode 100755
index 0000000..3b3ae1f
--- /dev/null
+++ b/t/t9818-git-p4-block.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+test_description='git p4 fetching changes in multiple blocks'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+create_restricted_group() {
+	p4 group -i <<-EOF
+	Group: restricted
+	MaxResults: 7
+	MaxScanRows: 40
+	Users: author
+	EOF
+}
+
+test_expect_success 'Create group with limited maxrows' '
+	create_restricted_group
+'
+
+test_expect_success 'Create a repo with many changes' '
+	(
+		client_view "//depot/included/... //client/included/..." \
+			    "//depot/excluded/... //client/excluded/..." &&
+		mkdir -p "$cli/included" "$cli/excluded" &&
+		cd "$cli/included" &&
+		>file.txt &&
+		p4 add file.txt &&
+		p4 submit -d "Add file.txt" &&
+		for i in $(test_seq 0 5)
+		do
+			>outer$i.txt &&
+			p4 add outer$i.txt &&
+			p4 submit -d "Adding outer$i.txt" &&
+			for j in $(test_seq 0 5)
+			do
+				p4 edit file.txt &&
+				echo $i$j >file.txt &&
+				p4 submit -d "Commit $i$j" || exit
+			done || exit
+		done
+	)
+'
+
+test_expect_success 'Default user cannot fetch changes' '
+	! p4 changes -m 1 //depot/...
+'
+
+test_expect_success 'Clone the repo' '
+	git p4 clone --dest="$git" --changes-block-size=7 --verbose //depot/included@all
+'
+
+test_expect_success 'All files are present' '
+	echo file.txt >expected &&
+	test_write_lines outer0.txt outer1.txt outer2.txt outer3.txt outer4.txt >>expected &&
+	test_write_lines outer5.txt >>expected &&
+	ls "$git" >current &&
+	test_cmp expected current
+'
+
+test_expect_success 'file.txt is correct' '
+	echo 55 >expected &&
+	test_cmp expected "$git/file.txt"
+'
+
+test_expect_success 'Correct number of commits' '
+	(cd "$git" && git log --oneline) >log &&
+	wc -l log &&
+	test_line_count = 43 log
+'
+
+test_expect_success 'Previous version of file.txt is correct' '
+	(cd "$git" && git checkout HEAD^^) &&
+	echo 53 >expected &&
+	test_cmp expected "$git/file.txt"
+'
+
+# Test git-p4 sync, with some files outside the client specification.
+
+p4_add_file() {
+	(cd "$cli" &&
+		>$1 &&
+		p4 add $1 &&
+		p4 submit -d "Added a file" $1
+	)
+}
+
+test_expect_success 'Add some more files' '
+	for i in $(test_seq 0 10)
+	do
+		p4_add_file "included/x$i" &&
+		p4_add_file "excluded/x$i"
+	done &&
+	for i in $(test_seq 0 10)
+	do
+		p4_add_file "excluded/y$i"
+	done
+'
+
+# This should pick up the 10 new files in "included", but not be confused
+# by the additional files in "excluded"
+test_expect_success 'Syncing files' '
+	(
+		cd "$git" &&
+		git p4 sync --changes-block-size=7 &&
+		git checkout p4/master &&
+		ls -l x* > log &&
+		test_line_count = 11 log
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh
new file mode 100755
index 0000000..78f1d0f
--- /dev/null
+++ b/t/t9819-git-p4-case-folding.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+test_description='interaction with P4 case-folding'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d with case folding enabled' '
+	start_p4d -C1
+'
+
+test_expect_success 'Create a repo, name is lowercase' '
+	(
+		client_view "//depot/... //client/..." &&
+		cd "$cli" &&
+		mkdir -p lc UC &&
+		>lc/file.txt && >UC/file.txt &&
+		p4 add lc/file.txt UC/file.txt &&
+		p4 submit -d "Add initial lc and UC repos"
+	)
+'
+
+test_expect_success 'Check p4 is in case-folding mode' '
+	(
+		cd "$cli" &&
+		>lc/FILE.TXT &&
+		p4 add lc/FILE.TXT &&
+		test_must_fail p4 submit -d "Cannot add file differing only in case" lc/FILE.TXT
+	)
+'
+
+# Check we created the repo properly
+test_expect_success 'Clone lc repo using lc name' '
+	git p4 clone //depot/lc/... &&
+	test_path_is_file lc/file.txt &&
+	git p4 clone //depot/UC/... &&
+	test_path_is_file UC/file.txt
+'
+
+# The clone should fail, since there is no repo called LC, but because
+# we have case-insensitive p4d enabled, it appears to go ahead and work,
+# but leaves an empty git repo in place.
+test_expect_failure 'Clone lc repo using uc name' '
+	test_must_fail git p4 clone //depot/LC/...
+'
+
+test_expect_failure 'Clone UC repo with lc name' '
+	test_must_fail git p4 clone //depot/uc/...
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9820-git-p4-editor-handling.sh b/t/t9820-git-p4-editor-handling.sh
new file mode 100755
index 0000000..6dc6df0
--- /dev/null
+++ b/t/t9820-git-p4-editor-handling.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git p4 handling of EDITOR'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+	start_p4d
+'
+
+test_expect_success 'init depot' '
+	(
+		cd "$cli" &&
+		echo file1 >file1 &&
+		p4 add file1 &&
+		p4 submit -d "file1"
+	)
+'
+
+# Check that the P4EDITOR argument can be given command-line
+# options, which git-p4 will then pass through to the shell.
+test_expect_success 'EDITOR with options' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		echo change >file1 &&
+		git commit -m "change" file1 &&
+		P4EDITOR=": >\"$git/touched\" && test-chmtime +5" git p4 submit &&
+		test_path_is_file "$git/touched"
+	)
+'
+
+test_expect_success 'kill p4d' '
+	kill_p4d
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 4a14a58..2ba62fb 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -370,6 +370,40 @@
 	test_cmp expect actual
 '
 
+test_expect_success '__git_get_config_variables' '
+	cat >expect <<-EOF &&
+	name-1
+	name-2
+	EOF
+	test_config interesting.name-1 good &&
+	test_config interesting.name-2 good &&
+	test_config subsection.interesting.name-3 bad &&
+	__git_get_config_variables interesting >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '__git_pretty_aliases' '
+	cat >expect <<-EOF &&
+	author
+	hash
+	EOF
+	test_config pretty.author "%an %ae" &&
+	test_config pretty.hash %H &&
+	__git_pretty_aliases >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '__git_aliases' '
+	cat >expect <<-EOF &&
+	ci
+	co
+	EOF
+	test_config alias.ci commit &&
+	test_config alias.co checkout &&
+	__git_aliases >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'basic' '
 	run_completion "git " &&
 	# built-in
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 8f8858a..e8d3c0f 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -348,11 +348,18 @@
 	return 1
 }
 
+test_verify_prereq () {
+	test -z "$test_prereq" ||
+	expr >/dev/null "$test_prereq" : '[A-Z0-9_,!]*$' ||
+	error "bug in the test script: '$test_prereq' does not look like a prereq"
+}
+
 test_expect_failure () {
 	test_start_
 	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
 	test "$#" = 2 ||
 	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+	test_verify_prereq
 	export test_prereq
 	if ! test_skip "$@"
 	then
@@ -372,6 +379,7 @@
 	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
 	test "$#" = 2 ||
 	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	test_verify_prereq
 	export test_prereq
 	if ! test_skip "$@"
 	then
@@ -400,6 +408,7 @@
 	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
 	descr="$1"
 	shift
+	test_verify_prereq
 	export test_prereq
 	if ! test_skip "$descr" "$@"
 	then
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 4ea99a2..cea6cda 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -15,9 +15,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see http://www.gnu.org/licenses/ .
 
-# Keep the original TERM for say_color
-ORIGINAL_TERM=$TERM
-
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
 if test -z "$TEST_DIRECTORY"
@@ -68,12 +65,12 @@
 esac
 
 # For repeatability, reset the environment to known value.
+# TERM is sanitized below, after saving color control sequences.
 LANG=C
 LC_ALL=C
 PAGER=cat
 TZ=UTC
-TERM=dumb
-export LANG LC_ALL PAGER TERM TZ
+export LANG LC_ALL PAGER TZ
 EDITOR=:
 # A call to "unset" with no arguments causes at least Solaris 10
 # /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets
@@ -181,8 +178,14 @@
 # This test checks if command xyzzy does the right thing...
 # '
 # . ./test-lib.sh
+test "x$TERM" != "xdumb" && (
+		test -t 1 &&
+		tput bold >/dev/null 2>&1 &&
+		tput setaf 1 >/dev/null 2>&1 &&
+		tput sgr0 >/dev/null 2>&1
+	) &&
+	color=t
 
-unset color
 while test "$#" -ne 0
 do
 	case "$1" in
@@ -253,6 +256,44 @@
 	verbose=t
 fi
 
+if test -n "$color"
+then
+	# Save the color control sequences now rather than run tput
+	# each time say_color() is called.  This is done for two
+	# reasons:
+	#   * TERM will be changed to dumb
+	#   * HOME will be changed to a temporary directory and tput
+	#     might need to read ~/.terminfo from the original HOME
+	#     directory to get the control sequences
+	# Note:  This approach assumes the control sequences don't end
+	# in a newline for any terminal of interest (command
+	# substitutions strip trailing newlines).  Given that most
+	# (all?) terminals in common use are related to ECMA-48, this
+	# shouldn't be a problem.
+	say_color_error=$(tput bold; tput setaf 1) # bold red
+	say_color_skip=$(tput setaf 4) # blue
+	say_color_warn=$(tput setaf 3) # brown/yellow
+	say_color_pass=$(tput setaf 2) # green
+	say_color_info=$(tput setaf 6) # cyan
+	say_color_reset=$(tput sgr0)
+	say_color_="" # no formatting for normal text
+	say_color () {
+		test -z "$1" && test -n "$quiet" && return
+		eval "say_color_color=\$say_color_$1"
+		shift
+		printf "%s\\n" "$say_color_color$*$say_color_reset"
+	}
+else
+	say_color() {
+		test -z "$1" && test -n "$quiet" && return
+		shift
+		printf "%s\n" "$*"
+	}
+fi
+
+TERM=dumb
+export TERM
+
 error () {
 	say_color error "error: $*"
 	GIT_EXIT_OK=t
@@ -529,7 +570,7 @@
 	test_cleanup=:
 	expecting_failure=$2
 
-	if test "${GIT_TEST_CHAIN_LINT:-0}" != 0; then
+	if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
 		# 117 is magic because it is unlikely to match the exit
 		# code of other programs
 		test_eval_ "(exit 117) && $1"
@@ -829,52 +870,6 @@
 GNUPGHOME="$HOME/gnupg-home-not-used"
 export HOME GNUPGHOME
 
-# run the tput tests *after* changing HOME (in case ncurses needs
-# ~/.terminfo for $TERM)
-test -n "${color+set}" || test "x$ORIGINAL_TERM" != "xdumb" && (
-		TERM=$ORIGINAL_TERM &&
-		export TERM &&
-		test -t 1 &&
-		tput bold >/dev/null 2>&1 &&
-		tput setaf 1 >/dev/null 2>&1 &&
-		tput sgr0 >/dev/null 2>&1
-	) &&
-	color=t
-
-if test -n "$color"
-then
-	say_color () {
-		(
-		TERM=$ORIGINAL_TERM
-		export TERM
-		case "$1" in
-		error)
-			tput bold; tput setaf 1;; # bold red
-		skip)
-			tput setaf 4;; # blue
-		warn)
-			tput setaf 3;; # brown/yellow
-		pass)
-			tput setaf 2;; # green
-		info)
-			tput setaf 6;; # cyan
-		*)
-			test -n "$quiet" && return;;
-		esac
-		shift
-		printf "%s" "$*"
-		tput sgr0
-		echo
-		)
-	}
-else
-	say_color() {
-		test -z "$1" && test -n "$quiet" && return
-		shift
-		printf "%s\n" "$*"
-	}
-fi
-
 if test -z "$TEST_NO_CREATE_REPO"
 then
 	test_create_repo "$TRASH_DIRECTORY"
diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample
index 8b2a2fe..a5d7b84 100755
--- a/templates/hooks--applypatch-msg.sample
+++ b/templates/hooks--applypatch-msg.sample
@@ -10,6 +10,6 @@
 # To enable this hook, rename this file to "applypatch-msg".
 
 . git-sh-setup
-test -x "$GIT_DIR/hooks/commit-msg" &&
-	exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
 :
diff --git a/templates/hooks--pre-applypatch.sample b/templates/hooks--pre-applypatch.sample
index b1f187c..4142082 100755
--- a/templates/hooks--pre-applypatch.sample
+++ b/templates/hooks--pre-applypatch.sample
@@ -9,6 +9,6 @@
 # To enable this hook, rename this file to "pre-applypatch".
 
 . git-sh-setup
-test -x "$GIT_DIR/hooks/pre-commit" &&
-	exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
 :
diff --git a/test-dump-untracked-cache.c b/test-dump-untracked-cache.c
new file mode 100644
index 0000000..25d855d
--- /dev/null
+++ b/test-dump-untracked-cache.c
@@ -0,0 +1,62 @@
+#include "cache.h"
+#include "dir.h"
+
+static int compare_untracked(const void *a_, const void *b_)
+{
+	const char *const *a = a_;
+	const char *const *b = b_;
+	return strcmp(*a, *b);
+}
+
+static int compare_dir(const void *a_, const void *b_)
+{
+	const struct untracked_cache_dir *const *a = a_;
+	const struct untracked_cache_dir *const *b = b_;
+	return strcmp((*a)->name, (*b)->name);
+}
+
+static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
+{
+	int i, len;
+	qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked),
+	      compare_untracked);
+	qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs),
+	      compare_dir);
+	len = base->len;
+	strbuf_addf(base, "%s/", ucd->name);
+	printf("%s %s", base->buf,
+	       sha1_to_hex(ucd->exclude_sha1));
+	if (ucd->recurse)
+		fputs(" recurse", stdout);
+	if (ucd->check_only)
+		fputs(" check_only", stdout);
+	if (ucd->valid)
+		fputs(" valid", stdout);
+	printf("\n");
+	for (i = 0; i < ucd->untracked_nr; i++)
+		printf("%s\n", ucd->untracked[i]);
+	for (i = 0; i < ucd->dirs_nr; i++)
+		dump(ucd->dirs[i], base);
+	strbuf_setlen(base, len);
+}
+
+int main(int ac, char **av)
+{
+	struct untracked_cache *uc;
+	struct strbuf base = STRBUF_INIT;
+	setup_git_directory();
+	if (read_cache() < 0)
+		die("unable to read index file");
+	uc = the_index.untracked;
+	if (!uc) {
+		printf("no untracked cache\n");
+		return 0;
+	}
+	printf("info/exclude %s\n", sha1_to_hex(uc->ss_info_exclude.sha1));
+	printf("core.excludesfile %s\n", sha1_to_hex(uc->ss_excludes_file.sha1));
+	printf("exclude_per_dir %s\n", uc->exclude_per_dir);
+	printf("flags %08x\n", uc->dir_flags);
+	if (uc->root)
+		dump(uc->root, &base);
+	return 0;
+}
diff --git a/trace.c b/trace.c
index 1dc5c7c..3c3bd8f 100644
--- a/trace.c
+++ b/trace.c
@@ -310,6 +310,7 @@
 		prefix = "(null)";
 
 	trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
+	trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
 	trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
 	trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
 	trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
diff --git a/transport.c b/transport.c
index eca9b8c..40692f8 100644
--- a/transport.c
+++ b/transport.c
@@ -278,12 +278,11 @@
 	return run_command(&rsync);
 }
 
-static int write_one_ref(const char *name, const unsigned char *sha1,
-		int flags, void *data)
+static int write_one_ref(const char *name, const struct object_id *oid,
+			 int flags, void *data)
 {
 	struct strbuf *buf = data;
 	int len = buf->len;
-	FILE *f;
 
 	/* when called via for_each_ref(), flags is non-zero */
 	if (flags && !starts_with(name, "refs/heads/") &&
@@ -292,27 +291,26 @@
 
 	strbuf_addstr(buf, name);
 	if (safe_create_leading_directories(buf->buf) ||
-			!(f = fopen(buf->buf, "w")) ||
-			fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
-			fclose(f))
-		return error("problems writing temporary file %s", buf->buf);
+	    write_file(buf->buf, 0, "%s\n", oid_to_hex(oid)))
+		return error("problems writing temporary file %s: %s",
+			     buf->buf, strerror(errno));
 	strbuf_setlen(buf, len);
 	return 0;
 }
 
 static int write_refs_to_temp_dir(struct strbuf *temp_dir,
-		int refspec_nr, const char **refspec)
+				  int refspec_nr, const char **refspec)
 {
 	int i;
 
 	for (i = 0; i < refspec_nr; i++) {
-		unsigned char sha1[20];
+		struct object_id oid;
 		char *ref;
 
-		if (dwim_ref(refspec[i], strlen(refspec[i]), sha1, &ref) != 1)
+		if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
 			return error("Could not get ref %s", refspec[i]);
 
-		if (write_one_ref(ref, sha1, 0, temp_dir)) {
+		if (write_one_ref(ref, &oid, 0, temp_dir)) {
 			free(ref);
 			return -1;
 		}
diff --git a/tree-diff.c b/tree-diff.c
index e7b378c..290a1da 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -64,7 +64,7 @@
 {
 	struct combine_diff_parent *p0 = &p->parent[0];
 	if (p->mode && p0->mode) {
-		opt->change(opt, p0->mode, p->mode, p0->sha1, p->sha1,
+		opt->change(opt, p0->mode, p->mode, p0->oid.hash, p->oid.hash,
 			1, 1, p->path, 0, 0);
 	}
 	else {
@@ -74,11 +74,11 @@
 
 		if (p->mode) {
 			addremove = '+';
-			sha1 = p->sha1;
+			sha1 = p->oid.hash;
 			mode = p->mode;
 		} else {
 			addremove = '-';
-			sha1 = p0->sha1;
+			sha1 = p0->oid.hash;
 			mode = p0->mode;
 		}
 
@@ -151,7 +151,7 @@
 	memcpy(p->path + base->len, path, pathlen);
 	p->path[len] = 0;
 	p->mode = mode;
-	hashcpy(p->sha1, sha1 ? sha1 : null_sha1);
+	hashcpy(p->oid.hash, sha1 ? sha1 : null_sha1);
 
 	return p;
 }
@@ -238,7 +238,7 @@
 			}
 
 			p->parent[i].mode = mode_i;
-			hashcpy(p->parent[i].sha1, sha1_i ? sha1_i : null_sha1);
+			hashcpy(p->parent[i].oid.hash, sha1_i ? sha1_i : null_sha1);
 		}
 
 		keep = 1;
diff --git a/tree-walk.c b/tree-walk.c
index 5dd9a71..6dccd2d 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -415,6 +415,12 @@
 	return error;
 }
 
+struct dir_state {
+	void *tree;
+	unsigned long size;
+	unsigned char sha1[20];
+};
+
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 {
 	int namelen = strlen(name);
@@ -478,6 +484,206 @@
 	return retval;
 }
 
+/*
+ * This is Linux's built-in max for the number of symlinks to follow.
+ * That limit, of course, does not affect git, but it's a reasonable
+ * choice.
+ */
+#define GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS 40
+
+/**
+ * Find a tree entry by following symlinks in tree_sha (which is
+ * assumed to be the root of the repository).  In the event that a
+ * symlink points outside the repository (e.g. a link to /foo or a
+ * root-level link to ../foo), the portion of the link which is
+ * outside the repository will be returned in result_path, and *mode
+ * will be set to 0.  It is assumed that result_path is uninitialized.
+ * If there are no symlinks, or the end result of the symlink chain
+ * points to an object inside the repository, result will be filled in
+ * with the sha1 of the found object, and *mode will hold the mode of
+ * the object.
+ *
+ * See the code for enum follow_symlink_result for a description of
+ * the return values.
+ */
+enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode)
+{
+	int retval = MISSING_OBJECT;
+	struct dir_state *parents = NULL;
+	size_t parents_alloc = 0;
+	ssize_t parents_nr = 0;
+	unsigned char current_tree_sha1[20];
+	struct strbuf namebuf = STRBUF_INIT;
+	struct tree_desc t;
+	int follows_remaining = GET_TREE_ENTRY_FOLLOW_SYMLINKS_MAX_LINKS;
+	int i;
+
+	init_tree_desc(&t, NULL, 0UL);
+	strbuf_init(result_path, 0);
+	strbuf_addstr(&namebuf, name);
+	hashcpy(current_tree_sha1, tree_sha1);
+
+	while (1) {
+		int find_result;
+		char *first_slash;
+		char *remainder = NULL;
+
+		if (!t.buffer) {
+			void *tree;
+			unsigned char root[20];
+			unsigned long size;
+			tree = read_object_with_reference(current_tree_sha1,
+							  tree_type, &size,
+							  root);
+			if (!tree)
+				goto done;
+
+			ALLOC_GROW(parents, parents_nr + 1, parents_alloc);
+			parents[parents_nr].tree = tree;
+			parents[parents_nr].size = size;
+			hashcpy(parents[parents_nr].sha1, root);
+			parents_nr++;
+
+			if (namebuf.buf[0] == '\0') {
+				hashcpy(result, root);
+				retval = FOUND;
+				goto done;
+			}
+
+			if (!size)
+				goto done;
+
+			/* descend */
+			init_tree_desc(&t, tree, size);
+		}
+
+		/* Handle symlinks to e.g. a//b by removing leading slashes */
+		while (namebuf.buf[0] == '/') {
+			strbuf_remove(&namebuf, 0, 1);
+		}
+
+		/* Split namebuf into a first component and a remainder */
+		if ((first_slash = strchr(namebuf.buf, '/'))) {
+			*first_slash = 0;
+			remainder = first_slash + 1;
+		}
+
+		if (!strcmp(namebuf.buf, "..")) {
+			struct dir_state *parent;
+			/*
+			 * We could end up with .. in the namebuf if it
+			 * appears in a symlink.
+			 */
+
+			if (parents_nr == 1) {
+				if (remainder)
+					*first_slash = '/';
+				strbuf_add(result_path, namebuf.buf,
+					   namebuf.len);
+				*mode = 0;
+				retval = FOUND;
+				goto done;
+			}
+			parent = &parents[parents_nr - 1];
+			free(parent->tree);
+			parents_nr--;
+			parent = &parents[parents_nr - 1];
+			init_tree_desc(&t, parent->tree, parent->size);
+			strbuf_remove(&namebuf, 0, remainder ? 3 : 2);
+			continue;
+		}
+
+		/* We could end up here via a symlink to dir/.. */
+		if (namebuf.buf[0] == '\0') {
+			hashcpy(result, parents[parents_nr - 1].sha1);
+			retval = FOUND;
+			goto done;
+		}
+
+		/* Look up the first (or only) path component in the tree. */
+		find_result = find_tree_entry(&t, namebuf.buf,
+					      current_tree_sha1, mode);
+		if (find_result) {
+			goto done;
+		}
+
+		if (S_ISDIR(*mode)) {
+			if (!remainder) {
+				hashcpy(result, current_tree_sha1);
+				retval = FOUND;
+				goto done;
+			}
+			/* Descend the tree */
+			t.buffer = NULL;
+			strbuf_remove(&namebuf, 0,
+				      1 + first_slash - namebuf.buf);
+		} else if (S_ISREG(*mode)) {
+			if (!remainder) {
+				hashcpy(result, current_tree_sha1);
+				retval = FOUND;
+			} else {
+				retval = NOT_DIR;
+			}
+			goto done;
+		} else if (S_ISLNK(*mode)) {
+			/* Follow a symlink */
+			unsigned long link_len;
+			size_t len;
+			char *contents, *contents_start;
+			struct dir_state *parent;
+			enum object_type type;
+
+			if (follows_remaining-- == 0) {
+				/* Too many symlinks followed */
+				retval = SYMLINK_LOOP;
+				goto done;
+			}
+
+			/*
+			 * At this point, we have followed at a least
+			 * one symlink, so on error we need to report this.
+			 */
+			retval = DANGLING_SYMLINK;
+
+			contents = read_sha1_file(current_tree_sha1, &type,
+						  &link_len);
+
+			if (!contents)
+				goto done;
+
+			if (contents[0] == '/') {
+				strbuf_addstr(result_path, contents);
+				free(contents);
+				*mode = 0;
+				retval = FOUND;
+				goto done;
+			}
+
+			if (remainder)
+				len = first_slash - namebuf.buf;
+			else
+				len = namebuf.len;
+
+			contents_start = contents;
+
+			parent = &parents[parents_nr - 1];
+			init_tree_desc(&t, parent->tree, parent->size);
+			strbuf_splice(&namebuf, 0, len,
+				      contents_start, link_len);
+			if (remainder)
+				namebuf.buf[link_len] = '/';
+			free(contents);
+		}
+	}
+done:
+	for (i = 0; i < parents_nr; i++)
+		free(parents[i].tree);
+	free(parents);
+
+	strbuf_release(&namebuf);
+	return retval;
+}
+
 static int match_entry(const struct pathspec_item *item,
 		       const struct name_entry *entry, int pathlen,
 		       const char *match, int matchlen,
diff --git a/tree-walk.h b/tree-walk.h
index ae7fb3a..3b2f7bf 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -40,6 +40,24 @@
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info);
 
+enum follow_symlinks_result {
+	FOUND = 0, /* This includes out-of-tree links */
+	MISSING_OBJECT = -1, /* The initial symlink is missing */
+	DANGLING_SYMLINK = -2, /*
+				* The initial symlink is there, but
+				* (transitively) points to a missing
+				* in-tree file
+				*/
+	SYMLINK_LOOP = -3,
+	NOT_DIR = -4, /*
+		       * Somewhere along the symlink chain, a path is
+		       * requested which contains a file as a
+		       * non-final element.
+		       */
+};
+
+enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode);
+
 struct traverse_info {
 	struct traverse_info *prev;
 	struct name_entry name;
diff --git a/unpack-trees.c b/unpack-trees.c
index be84ba2..2927660 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "attr.h"
 #include "split-index.h"
+#include "dir.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -1259,8 +1260,10 @@
 static void invalidate_ce_path(const struct cache_entry *ce,
 			       struct unpack_trees_options *o)
 {
-	if (ce)
-		cache_tree_invalidate_path(o->src_index, ce->name);
+	if (!ce)
+		return;
+	cache_tree_invalidate_path(o->src_index, ce->name);
+	untracked_cache_invalidate_path(o->src_index, ce->name);
 }
 
 /*
diff --git a/upload-pack.c b/upload-pack.c
index aa84576..89e832b 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,11 @@
 static int no_done;
 static int use_thin_pack, use_ofs_delta, use_include_tag;
 static int no_progress, daemon_mode;
-static int allow_tip_sha1_in_want;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1	01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1	02
+static unsigned int allow_unadvertised_object_request;
 static int shallow_nr;
 static struct object_array have_obj;
 static struct object_array want_obj;
@@ -74,7 +78,7 @@
 {
 	FILE *fp = cb_data;
 	if (graft->nr_parent == -1)
-		fprintf(fp, "--shallow %s\n", sha1_to_hex(graft->sha1));
+		fprintf(fp, "--shallow %s\n", oid_to_hex(&graft->oid));
 	return 0;
 }
 
@@ -442,8 +446,9 @@
 
 static int is_our_ref(struct object *o)
 {
-	return o->flags &
-		((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
+	int allow_hidden_ref = (allow_unadvertised_object_request &
+			(ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
+	return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
 }
 
 static void check_non_tip(void)
@@ -456,8 +461,12 @@
 	char namebuf[42]; /* ^ + SHA-1 + LF */
 	int i;
 
-	/* In the normal in-process case non-tip request can never happen */
-	if (!stateless_rpc)
+	/*
+	 * In the normal in-process case without
+	 * uploadpack.allowReachableSHA1InWant,
+	 * non-tip requests can never happen.
+	 */
+	if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
 		goto error;
 
 	cmd.argv = argv;
@@ -681,9 +690,9 @@
 }
 
 /* return non-zero if the ref is hidden, otherwise 0 */
-static int mark_our_ref(const char *refname, const unsigned char *sha1)
+static int mark_our_ref(const char *refname, const struct object_id *oid)
 {
-	struct object *o = lookup_unknown_object(sha1);
+	struct object *o = lookup_unknown_object(oid->hash);
 
 	if (ref_is_hidden(refname)) {
 		o->flags |= HIDDEN_REF;
@@ -693,9 +702,10 @@
 	return 0;
 }
 
-static int check_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int check_ref(const char *refname, const struct object_id *oid,
+		     int flag, void *cb_data)
 {
-	mark_our_ref(refname, sha1);
+	mark_our_ref(refname, oid);
 	return 0;
 }
 
@@ -709,48 +719,52 @@
 		strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
 }
 
-static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+static int send_ref(const char *refname, const struct object_id *oid,
+		    int flag, void *cb_data)
 {
 	static const char *capabilities = "multi_ack thin-pack side-band"
 		" side-band-64k ofs-delta shallow no-progress"
 		" include-tag multi_ack_detailed";
 	const char *refname_nons = strip_namespace(refname);
-	unsigned char peeled[20];
+	struct object_id peeled;
 
-	if (mark_our_ref(refname, sha1))
+	if (mark_our_ref(refname, oid))
 		return 0;
 
 	if (capabilities) {
 		struct strbuf symref_info = STRBUF_INIT;
 
 		format_symref_info(&symref_info, cb_data);
-		packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
-			     sha1_to_hex(sha1), refname_nons,
+		packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n",
+			     oid_to_hex(oid), refname_nons,
 			     0, capabilities,
-			     allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
+			     (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
+				     " allow-tip-sha1-in-want" : "",
+			     (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ?
+				     " allow-reachable-sha1-in-want" : "",
 			     stateless_rpc ? " no-done" : "",
 			     symref_info.buf,
 			     git_user_agent_sanitized());
 		strbuf_release(&symref_info);
 	} else {
-		packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
+		packet_write(1, "%s %s\n", oid_to_hex(oid), refname_nons);
 	}
 	capabilities = NULL;
-	if (!peel_ref(refname, peeled))
-		packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
+	if (!peel_ref(refname, peeled.hash))
+		packet_write(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
 	return 0;
 }
 
-static int find_symref(const char *refname, const unsigned char *sha1, int flag,
-		       void *cb_data)
+static int find_symref(const char *refname, const struct object_id *oid,
+		       int flag, void *cb_data)
 {
 	const char *symref_target;
 	struct string_list_item *item;
-	unsigned char unused[20];
+	struct object_id unused;
 
 	if ((flag & REF_ISSYMREF) == 0)
 		return 0;
-	symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
+	symref_target = resolve_ref_unsafe(refname, 0, unused.hash, &flag);
 	if (!symref_target || (flag & REF_ISSYMREF) == 0)
 		die("'%s' is a symref but it is not?", refname);
 	item = string_list_append(cb_data, refname);
@@ -787,9 +801,17 @@
 
 static int upload_pack_config(const char *var, const char *value, void *unused)
 {
-	if (!strcmp("uploadpack.allowtipsha1inwant", var))
-		allow_tip_sha1_in_want = git_config_bool(var, value);
-	else if (!strcmp("uploadpack.keepalive", var)) {
+	if (!strcmp("uploadpack.allowtipsha1inwant", var)) {
+		if (git_config_bool(var, value))
+			allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+		else
+			allow_unadvertised_object_request &= ~ALLOW_TIP_SHA1;
+	} else if (!strcmp("uploadpack.allowreachablesha1inwant", var)) {
+		if (git_config_bool(var, value))
+			allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
+		else
+			allow_unadvertised_object_request &= ~ALLOW_REACHABLE_SHA1;
+	} else if (!strcmp("uploadpack.keepalive", var)) {
 		keepalive = git_config_int(var, value);
 		if (!keepalive)
 			keepalive = -1;
diff --git a/utf8.h b/utf8.h
index e7b2aa4..5a9e94b 100644
--- a/utf8.h
+++ b/utf8.h
@@ -31,7 +31,9 @@
 			  const char *in_encoding,
 			  int *outsz);
 #else
-#define reencode_string_len(a,b,c,d,e) NULL
+static inline char *reencode_string_len(const char *a, int b,
+					const char *c, const char *d, int *e)
+{ if (e) *e = 0; return NULL; }
 #endif
 
 static inline char *reencode_string(const char *in,
diff --git a/walker.c b/walker.c
index 58ffeca..44a936c 100644
--- a/walker.c
+++ b/walker.c
@@ -200,9 +200,11 @@
 	return -1;
 }
 
-static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int mark_complete(const char *path, const struct object_id *oid,
+			 int flag, void *cb_data)
 {
-	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+
 	if (commit) {
 		commit->object.flags |= COMPLETE;
 		commit_list_insert(commit, &complete);
diff --git a/wrapper.c b/wrapper.c
index d5a6cef..ff49807 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -564,3 +564,39 @@
 		die_errno(_("unable to get current working directory"));
 	return strbuf_detach(&sb, NULL);
 }
+
+int write_file(const char *path, int fatal, const char *fmt, ...)
+{
+	struct strbuf sb = STRBUF_INIT;
+	va_list params;
+	int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+	if (fd < 0) {
+		if (fatal)
+			die_errno(_("could not open %s for writing"), path);
+		return -1;
+	}
+	va_start(params, fmt);
+	strbuf_vaddf(&sb, fmt, params);
+	va_end(params);
+	if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
+		int err = errno;
+		close(fd);
+		strbuf_release(&sb);
+		errno = err;
+		if (fatal)
+			die_errno(_("could not write to %s"), path);
+		return -1;
+	}
+	strbuf_release(&sb);
+	if (close(fd)) {
+		if (fatal)
+			die_errno(_("could not close %s"), path);
+		return -1;
+	}
+	return 0;
+}
+
+void sleep_millisec(int millisec)
+{
+	poll(NULL, 0, millisec);
+}
diff --git a/wt-status.c b/wt-status.c
index 38cb165..eaed4fe 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -585,6 +585,8 @@
 			DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
 	if (s->show_ignored_files)
 		dir.flags |= DIR_SHOW_IGNORED_TOO;
+	else
+		dir.untracked = the_index.untracked;
 	setup_standard_excludes(&dir);
 
 	fill_directory(&dir, &s->pathspec);
@@ -823,10 +825,11 @@
 	const char *p;
 	struct strbuf pattern = STRBUF_INIT;
 
-	strbuf_addf(&pattern, "%c %s", comment_line_char, cut_line);
-	p = strstr(buf->buf, pattern.buf);
-	if (p && (p == buf->buf || p[-1] == '\n'))
-		strbuf_setlen(buf, p - buf->buf);
+	strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
+	if (starts_with(buf->buf, pattern.buf + 1))
+		strbuf_setlen(buf, 0);
+	else if ((p = strstr(buf->buf, pattern.buf)))
+		strbuf_setlen(buf, p - buf->buf + 1);
 	strbuf_release(&pattern);
 }
 
@@ -1532,21 +1535,15 @@
 
 	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 */
+	if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) {
+		if (!base) {
+			fputc(s->null_termination ? '\0' : '\n', s->fp);
+			return;
+		}
+
 		upstream_is_gone = 1;
-		break;
-	default:
-		/* with base */
-		break;
 	}
 
-	base = branch->merge[0]->dst;
 	base = shorten_unambiguous_ref(base, 0);
 	color_fprintf(s->fp, header_color, "...");
 	color_fprintf(s->fp, branch_color_remote, "%s", base);