Merge branch 'jk/perf-aggregate-wo-libjson'

The script to aggregate perf result unconditionally depended on
libjson-perl even though it did not have to, which has been
corrected.

* jk/perf-aggregate-wo-libjson:
  t/perf: depend on perl JSON only when using --codespeed
diff --git a/.gitignore b/.gitignore
index 44c7440..2374f77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,7 +135,6 @@
 /git-remote-ftps
 /git-remote-fd
 /git-remote-ext
-/git-remote-testgit
 /git-remote-testpy
 /git-remote-testsvn
 /git-repack
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index bf2bf27..9022d48 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -14,3 +14,4 @@
 SubmittingPatches.txt
 tmp-doc-diff/
 GIT-ASCIIDOCFLAGS
+/GIT-EXCLUDED-PROGRAMS
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 6d738f8..dbf5a0f 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -7,7 +7,10 @@
 SP_ARTICLES =
 OBSOLETE_HTML =
 
+-include GIT-EXCLUDED-PROGRAMS
+
 MAN1_TXT += $(filter-out \
+		$(patsubst %,%.txt,$(EXCLUDED_PROGRAMS)) \
 		$(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
 		$(wildcard git-*.txt))
 MAN1_TXT += git.txt
diff --git a/Documentation/RelNotes/2.22.0.txt b/Documentation/RelNotes/2.22.0.txt
index d7d230e..776e155 100644
--- a/Documentation/RelNotes/2.22.0.txt
+++ b/Documentation/RelNotes/2.22.0.txt
@@ -49,6 +49,52 @@
  * "git mergetool" learned to offer Sublime Merge (smerge) as one of
    its backends.
 
+ * A new hook "post-index-change" is called when the on-disk index
+   file changes, which can help e.g. a virtualized working tree
+   implementation.
+
+ * "git difftool" can now run outside a repository.
+
+ * "git checkout -m <other>" was about carrying the differences
+   between HEAD and the working-tree files forward while checking out
+   another branch, and ignored the differences between HEAD and the
+   index.  The command has been taught to abort when the index and the
+   HEAD are different.
+
+ * A progress indicator has been added to the "index-pack" step, which
+   often makes users wait for completion during "git clone".
+
+ * "git submodule" learns "set-branch" subcommand that allows the
+   submodule.*.branch settings to be modified.
+
+ * "git merge-recursive" backend recently learned a new heuristics to
+   infer file movement based on how other files in the same directory
+   moved.  As this is inherently less robust heuristics than the one
+   based on the content similarity of the file itself (rather than
+   based on what its neighbours are doing), it sometimes gives an
+   outcome unexpected by the end users.  This has been toned down to
+   leave the renamed paths in higher/conflicted stages in the index so
+   that the user can examine and confirm the result.
+
+ * "git tag" learned to give an advice suggesting it might be a
+   mistake when creating an annotated or signed tag that points at
+   another tag.
+
+ * The "git pack-objects" command learned to report the number of
+   objects it packed via the trace2 mechanism.
+
+ * The list of conflicted paths shown in the editor while concluding a
+   conflicted merge was shown above the scissors line when the
+   clean-up mode is set to "scissors", even though it was commented
+   out just like the list of updated paths and other information to
+   help the user explain the merge better.
+
+ * The trace2 tracing facility learned to auto-generate a filename
+   when told to log to a directory.
+
+ * "git clone" learned a new --server-option option when talking over
+   the protocol version 2.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -94,6 +140,24 @@
    command getting non-unique when a new option that share the same
    prefix is added.
 
+ * The scripted version of "git rebase -i" wrote and rewrote the todo
+   list many times during a single step of its operation, and the
+   recent C-rewrite made a faithful conversion of the logic to C.  The
+   implementation has been updated to carry necessary information
+   around in-core to avoid rewriting the same file over and over
+   unnecessarily.
+
+ * Test framework update to more robustly clean up leftover files and
+   processes after tests are done.
+
+ * Conversion from unsigned char[20] to struct object_id continues.
+
+ * While running "git diff" in a lazy clone, we can upfront know which
+   missing blobs we will need, instead of waiting for the on-demand
+   machinery to discover them one by one.  The code learned to aim to
+   achieve better performance by batching the request for these
+   promised blobs.
+
 
 Fixes since v2.21
 -----------------
@@ -240,6 +304,138 @@
  * The documentation for "git read-tree --reset -u" has been updated.
    (merge b5a0bd694c nd/read-tree-reset-doc later to maint).
 
+ * Code clean-up around a much-less-important-than-it-used-to-be
+   update_server_info() funtion.
+   (merge b3223761c8 jk/server-info-rabbit-hole later to maint).
+
+ * The message given when "git commit -a <paths>" errors out has been
+   updated.
+   (merge 5a1dbd48bc nd/commit-a-with-paths-msg-update later to maint).
+
+ * "git cherry-pick --options A..B", after giving control back to the
+   user to ask help resolving a conflicted step, did not honor the
+   options it originally received, which has been corrected.
+
+ * Various glitches in "git gc" around reflog handling have been fixed.
+
+ * The code to read from commit-graph file has been cleanup with more
+   careful error checking before using data read from it.
+
+ * Performance fix around "git fetch" that grabs many refs.
+   (merge b764300912 jt/fetch-pack-wanted-refs-optim later to maint).
+
+ * Protocol v2 support in "git fetch-pack" of shallow clones has been
+   corrected.
+
+ * Performance fix around "git blame", especially in a linear history
+   (which is the norm we should optimize for).
+   (merge f892014943 dk/blame-keep-origin-blob later to maint).
+
+ * Performance fix for "rev-list --parents -- pathspec".
+   (merge 8320b1dbe7 jk/revision-rewritten-parents-in-prio-queue later to maint).
+
+ * Updating the display with progress message has been cleaned up to
+   deal better with overlong messages.
+   (merge 545dc345eb sg/overlong-progress-fix later to maint).
+
+ * "git blame -- path" in a non-bare repository starts blaming from
+   the working tree, and the same command in a bare repository errors
+   out because there is no working tree by definition.  The command
+   has been taught to instead start blaming from the commit at HEAD,
+   which is more useful.
+   (merge a544fb08f8 sg/blame-in-bare-start-at-head later to maint).
+
+ * An underallocation in the code to read the untracked cache
+   extension has been corrected.
+   (merge 3a7b45a623 js/untracked-cache-allocfix later to maint).
+
+ * The code is updated to check the result of memory allocation before
+   it is used in more places, by using xmalloc and/or xcalloc calls.
+   (merge 999b951b28 jk/xmalloc later to maint).
+
+ * The GETTEXT_POISON test option has been quite broken ever since it
+   was made runtime-tunable, which has been fixed.
+   (merge f88b9cb603 jc/gettext-test-fix later to maint).
+
+ * Test fix on APFS that is incapable of store paths in Latin-1.
+   (merge 3889149619 js/iso8895-test-on-apfs later to maint).
+
+ * "git submodule foreach <command> --quiet" did not pass the option
+   down correctly, which has been corrected.
+   (merge a282f5a906 nd/submodule-foreach-quiet later to maint).
+
+ * "git send-email" has been taught to use quoted-printable when the
+   payload contains carriage-return.  The use of the mechanism is in
+   line with the design originally added the codepath that chooses QP
+   when the payload has overly long lines.
+   (merge 74d76a1701 bc/send-email-qp-cr later to maint).
+
+ * The recently added feature to add addresses that are on
+   anything-by: trailers in 'git send-email' was found to be way too
+   eager and considered nonsense strings as if they can be legitimate
+   beginning of *-by: trailer.  This has been tightened.
+
+ * Builds with gettext broke on recent macOS w/ Homebrew, which
+   seems to have stopped including from /usr/local/include; this
+   has been corrected.
+   (merge 92a1377a2a js/macos-gettext-build later to maint).
+
+ * Running "git add" on a repository created inside the current
+   repository is an explicit indication that the user wants to add it
+   as a submodule, but when the HEAD of the inner repository is on an
+   unborn branch, it cannot be added as a submodule.  Worse, the files
+   in its working tree can be added as if they are a part of the outer
+   repository, which is not what the user wants.  These problems are
+   being addressed.
+   (merge f937bc2f86 km/empty-repo-is-still-a-repo later to maint).
+
+ * "git cherry-pick" run with the "-x" or the "--signoff" option used
+   to (and more importantly, ought to) clean up the commit log message
+   with the --cleanup=space option by default, but this has been
+   broken since late 2017.  This has been fixed.
+
+ * When given a tag that points at a commit-ish, "git replace --graft"
+   failed to peel the tag before writing a replace ref, which did not
+   make sense because the old graft mechanism the feature wants to
+   mimick only allowed to replace one commit object with another.
+   This has been fixed.
+   (merge ee521ec4cb cc/replace-graft-peel-tags later to maint).
+
+ * Code tightening against a "wrong" object appearing where an object
+   of a different type is expected, instead of blindly assuming that
+   the connection between objects are correctly made.
+   (merge 97dd512af7 tb/unexpected later to maint).
+
+ * An earlier update for MinGW and Cygwin accidentally broke MSVC build,
+   which has been fixed.
+   (merge 22c3634c0f ss/msvc-path-utils-fix later to maint).
+
+ * %(push:track) token used in the --format option to "git
+   for-each-ref" and friends was not showing the right branch, which
+   has been fixed.
+   (merge c646d0934e dr/ref-filter-push-track-fix later to maint).
+
+ * "make check-docs", "git help -a", etc. did not account for cases
+   where a particular build may deliberately omit some subcommands,
+   which has been corrected.
+
+ * The logic to tell if a Git repository has a working tree protects
+   "git branch -D" from removing the branch that is currently checked
+   out by mistake.  The implementation of this logic was broken for
+   repositories with unusual name, which unfortunately is the norm for
+   submodules these days.  This has been fixed.
+   (merge f3534c98e4 jt/submodule-repo-is-with-worktree later to maint).
+
+ * AIX shared the same build issues with other BSDs around fileno(fp),
+   which has been corrected.
+   (merge ee662bf5c6 cc/aix-has-fileno-as-a-macro later to maint).
+
+ * The autoconf generated configure script failed to use the right
+   gettext() implementations from -libintl by ignoring useless stub
+   implementations shipped in some C library, which has been
+   corrected.
+   (merge b71e56a683 vk/autoconf-gettext later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 11f470aee7 jc/test-yes-doc later to maint).
    (merge 90503a240b js/doc-symref-in-proto-v1 later to maint).
@@ -267,3 +463,11 @@
    (merge 0b918b75af sg/t5318-cleanup later to maint).
    (merge 68ed71b53c cb/doco-mono later to maint).
    (merge a34dca2451 nd/interpret-trailers-docfix later to maint).
+   (merge cf7b857a77 en/fast-import-parsing-fix later to maint).
+   (merge fe61ccbc35 po/rerere-doc-fmt later to maint).
+   (merge ffea0248bf po/describe-not-necessarily-7 later to maint).
+   (merge 7cb7283adb tg/ls-files-debug-format-fix later to maint).
+   (merge f64a21bd82 tz/doc-apostrophe-no-longer-needed later to maint).
+   (merge dbe7b41019 js/t3301-unbreak-notes-test later to maint).
+   (merge d8083e4180 km/t3000-retitle later to maint).
+   (merge 9e4cbccbd7 tz/git-svn-doc-markup-fix later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index d87846f..7e2a6f6 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -422,6 +422,8 @@
 
 include::config/tag.txt[]
 
+include::config/trace2.txt[]
+
 include::config/transfer.txt[]
 
 include::config/uploadarchive.txt[]
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index 8862042..ec4f6ae 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -90,4 +90,6 @@
 	waitingForEditor::
 		Print a message to the terminal whenever Git is waiting for
 		editor input from the user.
+	nestedTag::
+		Advice shown if a user attempts to recursively tag a tag object.
 --
diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt
index 73c08b0..02b92b1 100644
--- a/Documentation/config/gc.txt
+++ b/Documentation/config/gc.txt
@@ -1,25 +1,42 @@
 gc.aggressiveDepth::
 	The depth parameter used in the delta compression
 	algorithm used by 'git gc --aggressive'.  This defaults
-	to 50.
+	to 50, which is the default for the `--depth` option when
+	`--aggressive` isn't in use.
++
+See the documentation for the `--depth` option in
+linkgit:git-repack[1] for more details.
 
 gc.aggressiveWindow::
 	The window size parameter used in the delta compression
 	algorithm used by 'git gc --aggressive'.  This defaults
-	to 250.
+	to 250, which is a much more aggressive window size than
+	the default `--window` of 10.
++
+See the documentation for the `--window` option in
+linkgit:git-repack[1] for more details.
 
 gc.auto::
 	When there are approximately more than this many loose
 	objects in the repository, `git gc --auto` will pack them.
 	Some Porcelain commands use this command to perform a
 	light-weight garbage collection from time to time.  The
-	default value is 6700.  Setting this to 0 disables it.
+	default value is 6700.
++
+Setting this to 0 disables not only automatic packing based on the
+number of loose objects, but any other heuristic `git gc --auto` will
+otherwise use to determine if there's work to do, such as
+`gc.autoPackLimit`.
 
 gc.autoPackLimit::
 	When there are more than this many packs that are not
 	marked with `*.keep` file in the repository, `git gc
 	--auto` consolidates them into one larger pack.  The
 	default value is 50.  Setting this to 0 disables it.
+	Setting `gc.auto` to 0 will also disable this.
++
+See the `gc.bigPackThreshold` configuration variable below. When in
+use, it'll affect how the auto pack limit works.
 
 gc.autoDetach::
 	Make `git gc --auto` return immediately and run in background
@@ -36,11 +53,16 @@
 this configuration variable is ignored, all packs except the base pack
 will be repacked. After this the number of packs should go below
 gc.autoPackLimit and gc.bigPackThreshold should be respected again.
++
+If the amount of memory estimated for `git repack` to run smoothly is
+not available and `gc.bigPackThreshold` is not set, the largest pack
+will also be excluded (this is the equivalent of running `git gc` with
+`--keep-base-pack`).
 
 gc.writeCommitGraph::
 	If true, then gc will rewrite the commit-graph file when
-	linkgit:git-gc[1] is run. When using linkgit:git-gc[1]
-	'--auto' the commit-graph will be updated if housekeeping is
+	linkgit:git-gc[1] is run. When using `git gc --auto`
+	the commit-graph will be updated if housekeeping is
 	required. Default is false. See linkgit:git-commit-graph[1]
 	for details.
 
@@ -94,6 +116,12 @@
 	With "<pattern>" (e.g. "refs/stash")
 	in the middle, the setting applies only to the refs that
 	match the <pattern>.
++
+These types of entries are generally created as a result of using `git
+commit --amend` or `git rebase` and are the commits prior to the amend
+or rebase occurring.  Since these changes are not part of the current
+project most users will want to expire them sooner, which is why the
+default is more aggressive than `gc.reflogExpire`.
 
 gc.rerereResolved::
 	Records of conflicted merge you resolved earlier are
diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index d389c73..6a31393 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -39,9 +39,22 @@
 	is turned off.
 
 merge.renames::
-	Whether and how Git detects renames.  If set to "false",
-	rename detection is disabled. If set to "true", basic rename
-	detection is enabled.  Defaults to the value of diff.renames.
+	Whether Git detects renames.  If set to "false", rename detection
+	is disabled. If set to "true", basic rename detection is enabled.
+	Defaults to the value of diff.renames.
+
+merge.directoryRenames::
+	Whether Git detects directory renames, affecting what happens at
+	merge time to new files added to a directory on one side of
+	history when that directory was renamed on the other side of
+	history.  If merge.directoryRenames is set to "false", directory
+	rename detection is disabled, meaning that such new files will be
+	left behind in the old directory.  If set to "true", directory
+	rename detection is enabled, meaning that such new files will be
+	moved into the new directory.  If set to "conflict", a conflict
+	will be reported for such paths.  If merge.renames is false,
+	merge.directoryRenames is ignored and treated as false.  Defaults
+	to "conflict".
 
 merge.renormalize::
 	Tell Git that canonical representation of files in the
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 425c73a..9cdcfa7 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -124,6 +124,4 @@
 	bitmapped and non-bitmapped objects (e.g., when serving a fetch
 	between an older, bitmapped pack and objects that have been
 	pushed since the last gc). The downside is that it consumes 4
-	bytes per object of disk space, and that JGit's bitmap
-	implementation does not understand it, causing it to complain if
-	Git and JGit are used on the same repository. Defaults to false.
+	bytes per object of disk space. Defaults to true.
diff --git a/Documentation/config/repack.txt b/Documentation/config/repack.txt
index a5c3781..9c413e1 100644
--- a/Documentation/config/repack.txt
+++ b/Documentation/config/repack.txt
@@ -24,4 +24,4 @@
 	packs created for clones and fetches, at the cost of some disk
 	space and extra time spent on the initial repack.  This has
 	no effect if multiple packfiles are created.
-	Defaults to false.
+	Defaults to true on bare repos, false otherwise.
diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt
new file mode 100644
index 0000000..a5f409c
--- /dev/null
+++ b/Documentation/config/trace2.txt
@@ -0,0 +1,56 @@
+Trace2 config settings are only read from the system and global
+config files; repository local and worktree config files and `-c`
+command line arguments are not respected.
+
+trace2.normalTarget::
+	This variable controls the normal target destination.
+	It may be overridden by the `GIT_TR2` environment variable.
+	The following table shows possible values.
+
+trace2.perfTarget::
+	This variable controls the performance target destination.
+	It may be overridden by the `GIT_TR2_PERF` environment variable.
+	The following table shows possible values.
+
+trace2.eventTarget::
+	This variable controls the event target destination.
+	It may be overridden by the `GIT_TR2_EVENT` environment variable.
+	The following table shows possible values.
++
+include::../trace2-target-values.txt[]
+
+trace2.normalBrief::
+	Boolean.  When true `time`, `filename`, and `line` fields are
+	omitted from normal output.  May be overridden by the
+	`GIT_TR2_BRIEF` environment variable.  Defaults to false.
+
+trace2.perfBrief::
+	Boolean.  When true `time`, `filename`, and `line` fields are
+	omitted from PERF output.  May be overridden by the
+	`GIT_TR2_PERF_BRIEF` environment variable.  Defaults to false.
+
+trace2.eventBrief::
+	Boolean.  When true `time`, `filename`, and `line` fields are
+	omitted from event output.  May be overridden by the
+	`GIT_TR2_EVENT_BRIEF` environment variable.  Defaults to false.
+
+trace2.eventNesting::
+	Integer.  Specifies desired depth of nested regions in the
+	event output.  Regions deeper than this value will be
+	omitted.  May be overridden by the `GIT_TR2_EVENT_NESTING`
+	environment variable.  Defaults to 2.
+
+trace2.configParams::
+	A comma-separated list of patterns of "important" config
+	settings that should be recorded in the trace2 output.
+	For example, `core.*,remote.*.url` would cause the trace2
+	output to contain events listing each configured remote.
+	May be overridden by the `GIT_TR2_CONFIG_PARAMS` environment
+	variable.  Unset by default.
+
+trace2.destinationDebug::
+	Boolean.  When true Git will print error messages when a
+	trace target destination cannot be opened for writing.
+	By default, these errors are suppressed and tracing is
+	silently disabled.  May be overridden by the
+	`GIT_TR2_DST_DEBUG` environment variable.
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index fa0a315..91c4775 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -216,7 +216,8 @@
 --server-option=<option>::
 	Transmit the given string to the server when communicating using
 	protocol version 2.  The given string must not contain a NUL or LF
-	character.
+	character.  The server's handling of server options, including
+	unknown ones, is server-specific.
 	When multiple `--server-option=<option>` are given, they are all
 	sent to the other side in the order listed on the command line.
 
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index d64e724..754b16c 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -57,6 +57,13 @@
 	With this option, 'git cherry-pick' will let you edit the commit
 	message prior to committing.
 
+--cleanup=<mode>::
+	This option determines how the commit message will be cleaned up before
+	being passed on to the commit machinery. See linkgit:git-commit[1] for more
+	details. In particular, if the '<mode>' is given a value of `scissors`,
+	scissors will be appended to `MERGE_MSG` before being passed on in the case
+	of a conflict.
+
 -x::
 	When recording the commit, append a line that says
 	"(cherry picked from commit ...)" to the original commit
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 2fd1252..a0f14b5 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -131,6 +131,14 @@
 	is specified. This flag forces progress status even if the
 	standard error stream is not directed to a terminal.
 
+--server-option=<option>::
+	Transmit the given string to the server when communicating using
+	protocol version 2.  The given string must not contain a NUL or LF
+	character.  The server's handling of server options, including
+	unknown ones, is server-specific.
+	When multiple `--server-option=<option>` are given, they are all
+	sent to the other side in the order listed on the command line.
+
 --no-checkout::
 -n::
 	No checkout of HEAD is performed after the clone is complete.
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index ccdc5f8..a88f6ae 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -139,7 +139,7 @@
 
 The number of additional commits is the number
 of commits which would be displayed by "git log v1.0.4..parent".
-The hash suffix is "-g" + 7-char abbreviation for the tip commit
+The hash suffix is "-g" + unambiguous abbreviation for the tip commit
 of parent (which was `2414721b194453f058079d897d13c4e377f92dc6`).
 The "g" prefix stands for "git" and is used to allow describing the version of
 a software depending on the SCM the software is managed with. This is useful
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 43ab3b1..d65cdb3 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -422,7 +422,12 @@
 all `filemodify`, `filecopy`, `filerename` and `notemodify` commands in
 the same commit, as `filedeleteall` wipes the branch clean (see below).
 
-The `LF` after the command is optional (it used to be required).
+The `LF` after the command is optional (it used to be required).  Note
+that for reasons of backward compatibility, if the commit ends with a
+`data` command (i.e. it has has no `from`, `merge`, `filemodify`,
+`filedelete`, `filecopy`, `filerename`, `filedeleteall` or
+`notemodify` commands) then two `LF` commands may appear at the end of
+the command instead of just one.
 
 `author`
 ^^^^^^^^
@@ -966,10 +971,6 @@
 	'get-mark' SP ':' <idnum> LF
 ....
 
-This command can be used anywhere in the stream that comments are
-accepted.  In particular, the `get-mark` command can be used in the
-middle of a commit but not in the middle of a `data` command.
-
 See ``Responses To Commands'' below for details about how to read
 this output safely.
 
@@ -996,9 +997,10 @@
 	<contents> LF
 ====
 
-This command can be used anywhere in the stream that comments are
-accepted.  In particular, the `cat-blob` command can be used in the
-middle of a commit but not in the middle of a `data` command.
+This command can be used where a `filemodify` directive can appear,
+allowing it to be used in the middle of a commit.  For a `filemodify`
+using an inline directive, it can also appear right before the `data`
+directive.
 
 See ``Responses To Commands'' below for details about how to read
 this output safely.
@@ -1011,8 +1013,8 @@
 blob or tree from a previous commit for use in the current one (with
 `filemodify`).
 
-The `ls` command can be used anywhere in the stream that comments are
-accepted, including the middle of a commit.
+The `ls` command can also be used where a `filemodify` directive can
+appear, allowing it to be used in the middle of a commit.
 
 Reading from the active commit::
 	This form can only be used in the middle of a `commit`.
@@ -1396,6 +1398,13 @@
 to force recomputation of all deltas can significantly reduce the
 final packfile size (30-50% smaller can be quite typical).
 
+Instead of running `git repack` you can also run `git gc
+--aggressive`, which will also optimize other things after an import
+(e.g. pack loose refs). As noted in the "AGGRESSIVE" section in
+linkgit:git-gc[1] the `--aggressive` option will find new deltas with
+the `-f` option to linkgit:git-repack[1]. For the reasons elaborated
+on above using `--aggressive` after a fast-import is one of the few
+cases where it's known to be worthwhile.
 
 MEMORY UTILIZATION
 ------------------
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index a7c1b0f..247f765 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -20,17 +20,16 @@
 reflog, rerere metadata or stale working trees. May also update ancillary
 indexes such as the commit-graph.
 
-Users are encouraged to run this task on a regular basis within
-each repository to maintain good disk space utilization and good
-operating performance.
+When common porcelain operations that create objects are run, they
+will check whether the repository has grown substantially since the
+last maintenance, and if so run `git gc` automatically. See `gc.auto`
+below for how to disable this behavior.
 
-Some git commands may automatically run 'git gc'; see the `--auto` flag
-below for details. If you know what you're doing and all you want is to
-disable this behavior permanently without further considerations, just do:
-
-----------------------
-$ git config --global gc.auto 0
-----------------------
+Running `git gc` manually should only be needed when adding objects to
+a repository without regularly running such porcelain commands, to do
+a one-off repository optimization, or e.g. to clean up a suboptimal
+mass-import. See the "PACKFILE OPTIMIZATION" section in
+linkgit:git-fast-import[1] for more details on the import case.
 
 OPTIONS
 -------
@@ -40,35 +39,17 @@
 	space utilization and performance.  This option will cause
 	'git gc' to more aggressively optimize the repository at the expense
 	of taking much more time.  The effects of this optimization are
-	persistent, so this option only needs to be used occasionally; every
-	few hundred changesets or so.
+	mostly persistent. See the "AGGRESSIVE" section below for details.
 
 --auto::
 	With this option, 'git gc' checks whether any housekeeping is
 	required; if not, it exits without performing any work.
-	Some git commands run `git gc --auto` after performing
-	operations that could create many loose objects. Housekeeping
-	is required if there are too many loose objects or too many
-	packs in the repository.
 +
-If the number of loose objects exceeds the value of the `gc.auto`
-configuration variable, then all loose objects are combined into a
-single pack using `git repack -d -l`.  Setting the value of `gc.auto`
-to 0 disables automatic packing of loose objects.
+See the `gc.auto` option in the "CONFIGURATION" section below for how
+this heuristic works.
 +
-If the number of packs exceeds the value of `gc.autoPackLimit`,
-then existing packs (except those marked with a `.keep` file
-or over `gc.bigPackThreshold` limit)
-are consolidated into a single pack by using the `-A` option of
-'git repack'.
-If the amount of memory is estimated not enough for `git repack` to
-run smoothly and `gc.bigPackThreshold` is not set, the largest
-pack will also be excluded (this is the equivalent of running `git gc`
-with `--keep-base-pack`).
-Setting `gc.autoPackLimit` to 0 disables automatic consolidation of
-packs.
-+
-If houskeeping is required due to many loose objects or packs, all
+Once housekeeping is triggered by exceeding the limits of
+configuration options such as `gc.auto` and `gc.autoPackLimit`, all
 other housekeeping tasks (e.g. rerere, working trees, reflog...) will
 be performed as well.
 
@@ -96,69 +77,39 @@
 	`.keep` files are consolidated into a single pack. When this
 	option is used, `gc.bigPackThreshold` is ignored.
 
+AGGRESSIVE
+----------
+
+When the `--aggressive` option is supplied, linkgit:git-repack[1] will
+be invoked with the `-f` flag, which in turn will pass
+`--no-reuse-delta` to linkgit:git-pack-objects[1]. This will throw
+away any existing deltas and re-compute them, at the expense of
+spending much more time on the repacking.
+
+The effects of this are mostly persistent, e.g. when packs and loose
+objects are coalesced into one another pack the existing deltas in
+that pack might get re-used, but there are also various cases where we
+might pick a sub-optimal delta from a newer pack instead.
+
+Furthermore, supplying `--aggressive` will tweak the `--depth` and
+`--window` options passed to linkgit:git-repack[1]. See the
+`gc.aggressiveDepth` and `gc.aggressiveWindow` settings below. By
+using a larger window size we're more likely to find more optimal
+deltas.
+
+It's probably not worth it to use this option on a given repository
+without running tailored performance benchmarks on it. It takes a lot
+more time, and the resulting space/delta optimization may or may not
+be worth it. Not using this at all is the right trade-off for most
+users and their repositories.
+
 CONFIGURATION
 -------------
 
-The optional configuration variable `gc.reflogExpire` can be
-set to indicate how long historical entries within each branch's
-reflog should remain available in this repository.  The setting is
-expressed as a length of time, for example '90 days' or '3 months'.
-It defaults to '90 days'.
+The below documentation is the same as what's found in
+linkgit:git-config[1]:
 
-The optional configuration variable `gc.reflogExpireUnreachable`
-can be set to indicate how long historical reflog entries which
-are not part of the current branch should remain available in
-this repository.  These types of entries are generally created as
-a result of using `git commit --amend` or `git rebase` and are the
-commits prior to the amend or rebase occurring.  Since these changes
-are not part of the current project most users will want to expire
-them sooner.  This option defaults to '30 days'.
-
-The above two configuration variables can be given to a pattern.  For
-example, this sets non-default expiry values only to remote-tracking
-branches:
-
-------------
-[gc "refs/remotes/*"]
-	reflogExpire = never
-	reflogExpireUnreachable = 3 days
-------------
-
-The optional configuration variable `gc.rerereResolved` indicates
-how long records of conflicted merge you resolved earlier are
-kept.  This defaults to 60 days.
-
-The optional configuration variable `gc.rerereUnresolved` indicates
-how long records of conflicted merge you have not resolved are
-kept.  This defaults to 15 days.
-
-The optional configuration variable `gc.packRefs` determines if
-'git gc' runs 'git pack-refs'. This can be set to "notbare" to enable
-it within all non-bare repos or it can be set to a boolean value.
-This defaults to true.
-
-The optional configuration variable `gc.writeCommitGraph` determines if
-'git gc' should run 'git commit-graph write'. This can be set to a
-boolean value. This defaults to false.
-
-The optional configuration variable `gc.aggressiveWindow` controls how
-much time is spent optimizing the delta compression of the objects in
-the repository when the --aggressive option is specified.  The larger
-the value, the more time is spent optimizing the delta compression.  See
-the documentation for the --window option in linkgit:git-repack[1] for
-more details.  This defaults to 250.
-
-Similarly, the optional configuration variable `gc.aggressiveDepth`
-controls --depth option in linkgit:git-repack[1]. This defaults to 50.
-
-The optional configuration variable `gc.pruneExpire` controls how old
-the unreferenced loose objects have to be before they are pruned.  The
-default is "2 weeks ago".
-
-Optional configuration variable `gc.worktreePruneExpire` controls how
-old a stale working tree should be before `git worktree prune` deletes
-it. Default is "3 months ago".
-
+include::config/gc.txt[]
 
 NOTES
 -----
@@ -168,8 +119,8 @@
 particular, it will keep not only objects referenced by your current set
 of branches and tags, but also objects referenced by the index,
 remote-tracking branches, refs saved by 'git filter-branch' in
-refs/original/, or reflogs (which may reference commits in branches
-that were later amended or rewound).
+refs/original/, reflogs (which may reference commits in branches
+that were later amended or rewound), and anything else in the refs/* namespace.
 If you are expecting some objects to be deleted and they aren't, check
 all of those locations and decide whether it makes sense in your case to
 remove those references.
@@ -190,8 +141,7 @@
 
 However, these features fall short of a complete solution, so users who
 run commands concurrently have to live with some risk of corruption (which
-seems to be low in practice) unless they turn off automatic garbage
-collection with 'git config gc.auto 0'.
+seems to be low in practice).
 
 HOOKS
 -----
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 5298f1b..8461c0e 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -118,6 +118,7 @@
 	linkgit:git-status[1] `--short` or linkgit:git-diff[1]
 	`--name-status` for more user-friendly alternatives.
 +
+--
 This option identifies the file status with the following tags (followed by
 a space) at the start of each line:
 
@@ -128,6 +129,7 @@
 	C::	modified/changed
 	K::	to be killed
 	?::	other
+--
 
 -v::
 	Similar to `-t`, but use lowercase letters for files
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 7061d66..d271842 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -129,6 +129,10 @@
 	Instead of reading tree object(s) into the index, just empty
 	it.
 
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
 <tree-ish#>::
 	The id of the tree object(s) to be read/merged.
 
diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index df310d2..95763d7 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -24,7 +24,7 @@
 hand resolutions to their corresponding automerge results.
 
 [NOTE]
-You need to set the configuration variable rerere.enabled in order to
+You need to set the configuration variable `rerere.enabled` in order to
 enable this command.
 
 
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 6afccb2..0c82ca5 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -66,6 +66,13 @@
 	With this option, 'git revert' will not start the commit
 	message editor.
 
+--cleanup=<mode>::
+	This option determines how the commit message will be cleaned up before
+	being passed on to the commit machinery. See linkgit:git-commit[1] for more
+	details. In particular, if the '<mode>' is given a value of `scissors`,
+	scissors will be appended to `MERGE_MSG` before being passed on in the case
+	of a conflict.
+
 -n::
 --no-commit::
 	Usually the command automatically creates some commits with
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 4a01371..5cc2fce 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -167,7 +167,7 @@
 ------------------------------------------------
 
 These three branches all forked from a common commit, [master],
-whose commit message is "Add {apostrophe}git show-branch{apostrophe}".
+whose commit message is "Add \'git show-branch'".
 The "fixes" branch adds one commit "Introduce "reset type" flag to
 "git reset"". The "mhf" branch adds many other commits.
 The current branch is "master".
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index ac8c687..0ed5c24 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -15,6 +15,7 @@
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] <path>...)
 'git submodule' [--quiet] update [<options>] [--] [<path>...]
+'git submodule' [--quiet] set-branch [<options>] [--] <path>
 'git submodule' [--quiet] summary [<options>] [--] [<path>...]
 'git submodule' [--quiet] foreach [--recursive] <command>
 'git submodule' [--quiet] sync [--recursive] [--] [<path>...]
@@ -172,6 +173,12 @@
 If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
 --
+set-branch ((-d|--default)|(-b|--branch <branch>)) [--] <path>::
+	Sets the default remote tracking branch for the submodule. The
+	`--branch` option allows the remote branch to be specified. The
+	`--default` option removes the submodule.<name>.branch configuration
+	key, which causes the tracking branch to default to 'master'.
+
 summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] [<path>...]::
 	Show commit summary between the given commit (defaults to HEAD) and
 	working tree/index. For a submodule in question, a series of commits
@@ -259,13 +266,14 @@
 	This option is only valid for the deinit command. Unregister all
 	submodules in the working tree.
 
--b::
---branch::
+-b <branch>::
+--branch <branch>::
 	Branch of repository to add as submodule.
 	The name of the branch is recorded as `submodule.<name>.branch` in
 	`.gitmodules` for `update --remote`.  A special value of `.` is used to
 	indicate that the name of the branch in the submodule should be the
-	same name as the current branch in the current repository.
+	same name as the current branch in the current repository.  If the
+	option is not specified, it defaults to 'master'.
 
 -f::
 --force::
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 223788f..3071162 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -1100,10 +1100,10 @@
 	tags = tags/*/project-a:refs/remotes/project-a/tags/*
 ------------------------------------------------------------------------
 
-Keep in mind that the '\*' (asterisk) wildcard of the local ref
-(right of the ':') *must* be the farthest right path component;
+Keep in mind that the `*` (asterisk) wildcard of the local ref
+(right of the `:`) *must* be the farthest right path component;
 however the remote wildcard may be anywhere as long as it's an
-independent path component (surrounded by '/' or EOL).   This
+independent path component (surrounded by `/` or EOL).   This
 type of configuration is not automatically created by 'init' and
 should be manually entered with a text-editor or using 'git config'.
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 5bf653c..786e778 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -496,6 +496,24 @@
 from standard input. Exiting with non-zero status from this script prevent
 `git-p4 submit` from launching. Run `git-p4 submit --help` for details.
 
+post-index-change
+~~~~~~~~~~~~~~~~~
+
+This hook is invoked when the index is written in read-cache.c
+do_write_locked_index.
+
+The first parameter passed to the hook is the indicator for the
+working directory being updated.  "1" meaning working directory
+was updated or "0" when the working directory was not updated.
+
+The second parameter passed to the hook is the indicator for whether
+or not the index was updated and the skip-worktree bit could have
+changed.  "1" meaning skip-worktree bits could have been updated
+and "0" meaning they were not.
+
+Only one parameter should be set to "1" when the hook runs.  The hook
+running passing "1", "1" should not be possible.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 92a7d93..61876db 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -32,6 +32,13 @@
 updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be
 set to `no` at the beginning of them.
 
+--cleanup=<mode>::
+	This option determines how the merge message will be cleaned up before
+	commiting. See linkgit:git-commit[1] for more details. In addition, if
+	the '<mode>' is given a value of `scissors`, scissors will be appended
+	to `MERGE_MSG` before being passed on to the commit machinery in the
+	case of a merge conflict.
+
 --ff::
 	When the merge resolves as a fast-forward, only update the branch
 	pointer, without creating a merge commit.  This is the default
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 2337a99..82c1e57 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -65,7 +65,7 @@
 '@'::
   '@' alone is a shortcut for `HEAD`.
 
-'<refname>@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
+'[<refname>]@{<date>}', e.g. 'master@\{yesterday\}', 'HEAD@{5 minutes ago}'::
   A ref followed by the suffix '@' with a date specification
   enclosed in a brace
   pair (e.g. '\{yesterday\}', '{1 month 2 weeks 3 days 1 hour 1
@@ -95,7 +95,7 @@
   The construct '@{-<n>}' means the <n>th branch/commit checked out
   before the current one.
 
-'<branchname>@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
+'[<branchname>]@\{upstream\}', e.g. 'master@\{upstream\}', '@\{u\}'::
   The suffix '@\{upstream\}' to a branchname (short form '<branchname>@\{u\}')
   refers to the branch that the branch specified by branchname is set to build on
   top of (configured with `branch.<name>.remote` and
@@ -103,7 +103,7 @@
   current one. These suffixes are also accepted when spelled in uppercase, and
   they mean the same thing no matter the case.
 
-'<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
+'[<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
@@ -131,7 +131,7 @@
 This suffix is also accepted when spelled in uppercase, and means the same
 thing no matter the case.
 
-'<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
+'<rev>{caret}[<n>]', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   '<rev>{caret}'
@@ -139,7 +139,9 @@
   '<rev>{caret}0' means the commit itself and is used when '<rev>' is the
   object name of a tag object that refers to a commit object.
 
-'<rev>{tilde}<n>', e.g. 'master{tilde}3'::
+'<rev>{tilde}[<n>]', e.g. 'HEAD{tilde}, master{tilde}3'::
+  A suffix '{tilde}' to a revision parameter means the first parent of
+  that commit object.
   A suffix '{tilde}<n>' to a revision parameter means the commit
   object that is the <n>th generation ancestor of the named
   commit object, following only the first parents.  I.e. '<rev>{tilde}3' is
@@ -159,12 +161,12 @@
   '<rev>{caret}0'
   is a short-hand for '<rev>{caret}\{commit\}'.
 +
-'rev{caret}\{object\}' can be used to make sure 'rev' names an
-object that exists, without requiring 'rev' to be a tag, and
-without dereferencing 'rev'; because a tag is already an object,
+'<rev>{caret}\{object\}' can be used to make sure '<rev>' names an
+object that exists, without requiring '<rev>' to be a tag, and
+without dereferencing '<rev>'; because a tag is already an object,
 it does not have to be dereferenced even once to get to an object.
 +
-'rev{caret}\{tag\}' can be used to ensure that 'rev' identifies an
+'<rev>{caret}\{tag\}' can be used to ensure that '<rev>' identifies an
 existing tag object.
 
 '<rev>{caret}{}', e.g. 'v0.99.8{caret}{}'::
@@ -194,19 +196,16 @@
   Depending on the given text, the shell's word splitting rules might
   require additional quoting.
 
-'<rev>:<path>', e.g. 'HEAD:README', ':README', 'master:./README'::
+'<rev>:<path>', e.g. 'HEAD:README', 'master:./README'::
   A suffix ':' followed by a path names the blob or tree
   at the given path in the tree-ish object named by the part
   before the colon.
-  ':path' (with an empty part before the colon)
-  is a special case of the syntax described next: content
-  recorded in the index at the given path.
   A path starting with './' or '../' is relative to the current working directory.
   The given path will be converted to be relative to the working tree's root directory.
   This is most useful to address a blob or tree from a commit or tree that has
   the same tree structure as the working tree.
 
-':<n>:<path>', e.g. ':0:README', ':README'::
+':[<n>:]<path>', e.g. ':0:README', ':README'::
   A colon, optionally followed by a stage number (0 to 3) and a
   colon, followed by a path, names a blob object in the
   index at the given path. A missing stage number (and the colon
@@ -302,7 +301,7 @@
 The 'r1{caret}!' notation includes commit 'r1' but excludes all of its parents.
 By itself, this notation denotes the single commit 'r1'.
 
-The '<rev>{caret}-<n>' notation includes '<rev>' but excludes the <n>th
+The '<rev>{caret}-[<n>]' notation includes '<rev>' but excludes the <n>th
 parent (i.e. a shorthand for '<rev>{caret}<n>..<rev>'), with '<n>' = 1 if
 not given. This is typically useful for merge commits where you
 can just pass '<commit>{caret}-' to get all the commits in the branch
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 2b036d7..2e2e7c1 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -198,8 +198,10 @@
 	The filename will be prefixed by passing the filename along with
 	the prefix argument of `parse_options()` to `prefix_filename()`.
 
-`OPT_ARGUMENT(long, description)`::
+`OPT_ARGUMENT(long, &int_var, description)`::
 	Introduce a long-option argument that will be kept in `argv[]`.
+	If this option was seen, `int_var` will be set to one (except
+	if a `NULL` pointer was passed).
 
 `OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
 	Recognize numerical options like -123 and feed the integer as
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index 2de565f..9e585b8 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -22,21 +22,41 @@
 formats in the future.  This might be used to define a binary format,
 for example.
 
+Trace2 is controlled using `trace2.*` config values in the system and
+global config files and `GIT_TR2*` environment variables.  Trace2 does
+not read from repo local or worktree config files or respect `-c`
+command line config settings.
+
 == Trace2 Targets
 
 Trace2 defines the following set of Trace2 Targets.
 Format details are given in a later section.
 
-`GIT_TR2` (NORMAL)::
+=== The Normal Format Target
 
-	a simple printf format like GIT_TRACE.
-+
+The normal format target is a tradition printf format and similar
+to GIT_TRACE format.  This format is enabled with the `GIT_TR`
+environment variable or the `trace2.normalTarget` system or global
+config setting.
+
+For example
+
 ------------
 $ export GIT_TR2=~/log.normal
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
+
+or
+
+------------
+$ git config --global trace2.normalTarget ~/log.normal
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
+
+yields
+
 ------------
 $ cat ~/log.normal
 12:28:42.620009 common-main.c:38                  version 2.20.1.155.g426c96fcdb
@@ -46,76 +66,86 @@
 12:28:42.621250 trace2/tr2_tgt_normal.c:124       atexit elapsed:0.001265 code:0
 ------------
 
-`GIT_TR2_PERF` (PERF)::
+=== The Performance Format Target
 
-	a column-based format to replace GIT_TRACE_PERFORMANCE suitable for
-	development and testing, possibly to complement tools like gprof.
-+
+The performance format target (PERF) is a column-based format to
+replace GIT_TRACE_PERFORMANCE and is suitable for development and
+testing, possibly to complement tools like gprof.  This format is
+enabled with the `GIT_TR2_PERF` environment variable or the
+`trace2.perfTarget` system or global config setting.
+
+For example
+
 ------------
 $ export GIT_TR2_PERF=~/log.perf
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
+
+or
+
+------------
+$ git config --global trace2.perfTarget ~/log.perf
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
+
+yields
+
 ------------
 $ cat ~/log.perf
 12:28:42.620675 common-main.c:38                  | d0 | main                     | version      |     |           |           |            | 2.20.1.155.g426c96fcdb
-12:28:42.621001 common-main.c:39                  | d0 | main                     | start        |     |           |           |            | git version
+12:28:42.621001 common-main.c:39                  | d0 | main                     | start        |     |  0.001173 |           |            | git version
 12:28:42.621111 git.c:432                         | d0 | main                     | cmd_name     |     |           |           |            | version (version)
 12:28:42.621225 git.c:662                         | d0 | main                     | exit         |     |  0.001227 |           |            | code:0
 12:28:42.621259 trace2/tr2_tgt_perf.c:211         | d0 | main                     | atexit       |     |  0.001265 |           |            | code:0
 ------------
 
-`GIT_TR2_EVENT` (EVENT)::
+=== The Event Format Target
 
-	a JSON-based format of event data suitable for telemetry analysis.
-+
+The event format target is a JSON-based format of event data suitable
+for telemetry analysis.  This format is enabled with the `GIT_TR2_EVENT`
+environment variable or the `trace2.eventTarget` system or global config
+setting.
+
+For example
+
 ------------
 $ export GIT_TR2_EVENT=~/log.event
 $ git version
 git version 2.20.1.155.g426c96fcdb
 ------------
-+
+
+or
+
+------------
+$ git config --global trace2.eventTarget ~/log.event
+$ git version
+git version 2.20.1.155.g426c96fcdb
+------------
+
+yields
+
 ------------
 $ cat ~/log.event
-{"event":"version","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.620713","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"}
-{"event":"start","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621027","file":"common-main.c","line":39,"argv":["git","version"]}
-{"event":"cmd_name","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621122","file":"git.c","line":432,"name":"version","hierarchy":"version"}
-{"event":"exit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621236","file":"git.c","line":662,"t_abs":0.001227,"code":0}
-{"event":"atexit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621268","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0}
+{"event":"version","sid":"sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"}
+{"event":"start","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621027Z","file":"common-main.c","line":39,"t_abs":0.001173,"argv":["git","version"]}
+{"event":"cmd_name","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621122Z","file":"git.c","line":432,"name":"version","hierarchy":"version"}
+{"event":"exit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621236Z","file":"git.c","line":662,"t_abs":0.001227,"code":0}
+{"event":"atexit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621268Z","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0}
 ------------
 
-== Enabling a Target
+=== Enabling a Target
 
-A Trace2 Target is enabled when the corresponding environment variable
-(`GIT_TR2`, `GIT_TR2_PERF`, or `GIT_TR2_EVENT`) is set.  The following
-values are recognized.
+To enable a target, set the corresponding environment variable or
+system or global config value to one of the following:
 
-`0`::
-`false`::
+include::../trace2-target-values.txt[]
 
-	Disables the target.
-
-`1`::
-`true`::
-
-	Enables the target and writes stream to `STDERR`.
-
-`[2-9]`::
-
-	Enables the target and writes to the already opened file descriptor.
-
-`<absolute-pathname>`::
-
-	Enables the target, opens and writes to the file in append mode.
-
-`af_unix:[<socket_type>:]<absolute-pathname>`::
-
-	Enables the target, opens and writes to a Unix Domain Socket
-	(on platforms that support them).
-+
-Socket type can be either `stream` or `dgram`.  If the socket type is
-omitted, Git will try both.
+If the target already exists and is a directory, the traces will be
+written to files (one per process) underneath the given directory. They
+will be named according to the last component of the SID (optionally
+followed by a counter to avoid filename collisions).
 
 == Trace2 API
 
@@ -160,17 +190,23 @@
 
 These are concerned with the lifetime of the overall git process.
 
+`void trace2_initialize_clock()`::
+
+	Initialize the Trace2 start clock and nothing else.  This should
+	be called at the very top of main() to capture the process start
+	time and reduce startup order dependencies.
+
 `void trace2_initialize()`::
 
 	Determines if any Trace2 Targets should be enabled and
-	initializes the Trace2 facility.  This includes starting the
-	elapsed time clocks and thread local storage (TLS).
+	initializes the Trace2 facility.  This includes setting up the
+	Trace2 thread local storage (TLS).
 +
 This function emits a "version" message containing the version of git
 and the Trace2 protocol.
 +
 This function should be called from `main()` as early as possible in
-the life of the process.
+the life of the process after essential process initialization.
 
 `int trace2_is_enabled()`::
 
@@ -237,15 +273,16 @@
 	Emits a "def_param" messages for "important" configuration
 	settings.
 +
-The environment variable `GIT_TR2_CONFIG_PARAMS` can be set to a
+The environment variable `GIT_TR2_CONFIG_PARAMS` or the `trace2.configParams`
+config value can be set to a
 list of patterns of important configuration settings, for example:
 `core.*,remote.*.url`.  This function will iterate over all config
 settings and emit a "def_param" message for each match.
 
 `void trace2_cmd_set_config(const char *key, const char *value)`::
 
-	Emits a "def_param" message for a specific configuration
-	setting IFF it matches the `GIT_TR2_CONFIG_PARAMS` pattern.
+	Emits a "def_param" message for a new or updated key/value
+	pair IF `key` is considered important.
 +
 This is used to hook into `git_config_set()` and catch any
 configuration changes and update a value previously reported by
@@ -412,9 +449,6 @@
 
 === NORMAL Format
 
-NORMAL format is enabled when the `GIT_TR2` environment variable is
-set.
-
 Events are written as lines of the form:
 
 ------------
@@ -431,8 +465,8 @@
 Note that this may contain embedded LF or CRLF characters that are
 not escaped, so the event may spill across multiple lines.
 
-If `GIT_TR2_BRIEF` is true, the `time`, `filename`, and `line` fields
-are omitted.
+If `GIT_TR2_BRIEF` or `trace2.normalBrief` is true, the `time`, `filename`,
+and `line` fields are omitted.
 
 This target is intended to be more of a summary (like GIT_TRACE) and
 less detailed than the other targets.  It ignores thread, region, and
@@ -440,9 +474,6 @@
 
 === PERF Format
 
-PERF format is enabled when the `GIT_TR2_PERF` environment variable
-is set.
-
 Events are written as lines of the form:
 
 ------------
@@ -502,8 +533,8 @@
 15:33:33.532712 wt-status.c:2331                  | d0 | main                     | region_leave | r1  |  0.127568 |  0.001504 | status     | label:print
 ------------
 
-If `GIT_TR2_PERF_BRIEF` is true, the `time`, `file`, and `line`
-fields are omitted.
+If `GIT_TR2_PERF_BRIEF` or `trace2.perfBrief` is true, the `time`, `file`,
+and `line` fields are omitted.
 
 ------------
 d0 | main                     | region_leave | r1  |  0.011717 |  0.009122 | index      | label:preload
@@ -514,9 +545,6 @@
 
 === EVENT Format
 
-EVENT format is enabled when the `GIT_TR2_EVENT` environment
-variable is set.
-
 Each event is a JSON-object containing multiple key/value pairs
 written as a single line and followed by a LF.
 
@@ -534,11 +562,11 @@
 ------------
 {
 	"event":"version",
-	"sid":"1547659722619736-11614",
+	"sid":"20190408T191827.272759Z-H9b68c35f-P00003510",
 	"thread":"main",
-	"time":"2019-01-16 17:28:42.620713",
+	"time":"2019-04-08T19:18:27.282761Z",
 	"file":"common-main.c",
-	"line":38,
+	"line":42,
 	...
 }
 ------------
@@ -570,9 +598,9 @@
 `"repo":<repo-id>`::
 	when present, is the integer repo-id as described previously.
 
-If `GIT_TR2_EVENT_BRIEF` is true, the `file` and `line` fields are omitted
-from all events and the `time` field is only present on the "start" and
-"atexit" events.
+If `GIT_TR2_EVENT_BRIEF` or `trace2.eventBrief` is true, the `file`
+and `line` fields are omitted from all events and the `time` field is
+only present on the "start" and "atexit" events.
 
 ==== Event-Specific Key/Value Pairs
 
@@ -595,6 +623,7 @@
 {
 	"event":"start",
 	...
+	"t_abs":0.001227, # elapsed time in seconds
 	"argv":["git","version"]
 }
 ------------
@@ -882,7 +911,7 @@
 The `category` field may be used in a future enhancement to
 do category-based filtering.
 +
-The `GIT_TR2_EVENT_NESTING` environment variable can be used to
+`GIT_TR2_EVENT_NESTING` or `trace2.eventNesting` can be used to
 filter deeply nested regions and data events.  It defaults to "2".
 
 `"region_leave"`::
@@ -1112,7 +1141,7 @@
 
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.160.g5676107ecd.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 ...
@@ -1157,7 +1186,7 @@
 ...
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.162.gb4ccea44db.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 ...
@@ -1213,7 +1242,7 @@
 ...
 $ cat ~/log.perf
 d0 | main                     | version      |     |           |           |            | 2.20.1.156.gf9916ae094.dirty
-d0 | main                     | start        |     |           |           |            | git status
+d0 | main                     | start        |     |  0.001173 |           |            | git status
 d0 | main                     | def_repo     | r1  |           |           |            | worktree:/Users/jeffhost/work/gfw
 d0 | main                     | cmd_name     |     |           |           |            | status (status)
 d0 | main                     | region_enter | r1  |  0.001791 |           | index      | label:do_read_index .git/index
diff --git a/Documentation/trace2-target-values.txt b/Documentation/trace2-target-values.txt
new file mode 100644
index 0000000..27d3c64
--- /dev/null
+++ b/Documentation/trace2-target-values.txt
@@ -0,0 +1,10 @@
+--
+* `0` or `false` - Disables the target.
+* `1` or `true` - Writes to `STDERR`.
+* `[2-9]` - Writes to the already opened file descriptor.
+* `<absolute-pathname>` - Writes to the file in append mode.
+* `af_unix:[<socket_type>:]<absolute-pathname>` - Write to a
+Unix DomainSocket (on platforms that support them).  Socket
+type can be either `stream` or `dgram`; if omitted Git will
+try both.
+--
diff --git a/Makefile b/Makefile
index 9f1b6e8..6d433f8 100644
--- a/Makefile
+++ b/Makefile
@@ -592,6 +592,7 @@
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
+EXCLUDED_PROGRAMS =
 SCRIPT_PERL =
 SCRIPT_PYTHON =
 SCRIPT_SH =
@@ -614,7 +615,6 @@
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-legacy-stash.sh
-SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -637,17 +637,11 @@
 
 SCRIPT_PYTHON += git-p4.py
 
-NO_INSTALL += git-remote-testgit
-
 # Generated files for scripts
 SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
 SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
 SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
 
-SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
-SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
-SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
-
 # Individual rules to allow e.g.
 # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
 # from subdirectories like contrib/*/
@@ -657,11 +651,11 @@
 build-python-script: $(SCRIPT_PYTHON_GEN)
 
 .PHONY: install-perl-script install-sh-script install-python-script
-install-sh-script: $(SCRIPT_SH_INS)
+install-sh-script: $(SCRIPT_SH_GEN)
 	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-perl-script: $(SCRIPT_PERL_INS)
+install-perl-script: $(SCRIPT_PERL_GEN)
 	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-python-script: $(SCRIPT_PYTHON_INS)
+install-python-script: $(SCRIPT_PYTHON_GEN)
 	$(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 
 .PHONY: clean-perl-script clean-sh-script clean-python-script
@@ -672,9 +666,9 @@
 clean-python-script:
 	$(RM) $(SCRIPT_PYTHON_GEN)
 
-SCRIPTS = $(SCRIPT_SH_INS) \
-	  $(SCRIPT_PERL_INS) \
-	  $(SCRIPT_PYTHON_INS) \
+SCRIPTS = $(SCRIPT_SH_GEN) \
+	  $(SCRIPT_PERL_GEN) \
+	  $(SCRIPT_PYTHON_GEN) \
 	  git-instaweb
 
 ETAGS_TARGET = TAGS
@@ -744,6 +738,7 @@
 TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
+TEST_BUILTINS_OBJS += test-serve-v2.o
 TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
 TEST_BUILTINS_OBJS += test-sha256.o
@@ -822,12 +817,12 @@
 
 GENERATED_H += command-list.h
 
-LIB_H := $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
+LIB_H := $(sort $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
 	$(FIND) . \
 	-name .git -prune -o \
 	-name t -prune -o \
 	-name Documentation -prune -o \
-	-name '*.h' -print)
+	-name '*.h' -print))
 
 LIB_OBJS += abspath.o
 LIB_OBJS += advice.o
@@ -1004,6 +999,7 @@
 LIB_OBJS += trace2/tr2_cmd_name.o
 LIB_OBJS += trace2/tr2_dst.o
 LIB_OBJS += trace2/tr2_sid.o
+LIB_OBJS += trace2/tr2_sysenv.o
 LIB_OBJS += trace2/tr2_tbuf.o
 LIB_OBJS += trace2/tr2_tgt_event.o
 LIB_OBJS += trace2/tr2_tgt_normal.o
@@ -1125,7 +1121,6 @@
 BUILTIN_OBJS += builtin/revert.o
 BUILTIN_OBJS += builtin/rm.o
 BUILTIN_OBJS += builtin/send-pack.o
-BUILTIN_OBJS += builtin/serve.o
 BUILTIN_OBJS += builtin/shortlog.o
 BUILTIN_OBJS += builtin/show-branch.o
 BUILTIN_OBJS += builtin/show-index.o
@@ -1342,6 +1337,7 @@
 	REMOTE_CURL_PRIMARY =
 	REMOTE_CURL_ALIASES =
 	REMOTE_CURL_NAMES =
+	EXCLUDED_PROGRAMS += git-http-fetch git-http-push
 else
 	ifdef CURLDIR
 		# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -1366,7 +1362,11 @@
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
 			PROGRAM_OBJS += http-push.o
+		else
+			EXCLUDED_PROGRAMS += git-http-push
 		endif
+	else
+		EXCLUDED_PROGRAMS += git-http-push
 	endif
 	curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "072200"
@@ -1614,6 +1614,7 @@
 endif
 ifdef NO_UNIX_SOCKETS
 	BASIC_CFLAGS += -DNO_UNIX_SOCKETS
+	EXCLUDED_PROGRAMS += git-credential-cache git-credential-cache--daemon
 else
 	LIB_OBJS += unix-socket.o
 	PROGRAM_OBJS += credential-cache.o
@@ -2133,7 +2134,9 @@
 command-list.h: generate-cmdlist.sh command-list.txt
 
 command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
-	$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@
+	$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
+		$(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
+		command-list.txt >$@+ && mv $@+ $@
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
 	$(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@ -2466,6 +2469,14 @@
 
 export DEFAULT_EDITOR DEFAULT_PAGER
 
+Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
+	@EXCLUDED='EXCLUDED_PROGRAMS := $(EXCLUDED_PROGRAMS)'; \
+	    if test x"$$EXCLUDED" != \
+		x"`cat Documentation/GIT-EXCLUDED-PROGRAMS 2>/dev/null`" ; then \
+		echo >&2 "    * new documentation flags"; \
+		echo "$$EXCLUDED" >Documentation/GIT-EXCLUDED-PROGRAMS; \
+            fi
+
 .PHONY: doc man man-perl html info pdf
 doc: man-perl
 	$(MAKE) -C Documentation all
@@ -2704,7 +2715,6 @@
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
-all:: $(NO_INSTALL)
 
 bin-wrappers/%: wrap-for-bin.sh
 	@mkdir -p bin-wrappers
@@ -2991,7 +3001,7 @@
 
 artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
 		GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
-		$(NO_INSTALL) $(MOFILES)
+		$(MOFILES)
 	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
 		SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 	test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3040,7 +3050,7 @@
 	$(RM) $(OBJECTS)
 	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
-	$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
 	$(RM) -r bin-wrappers $(dep_dirs)
 	$(RM) -r po/build/
@@ -3049,6 +3059,7 @@
 	$(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
 	$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
 	$(MAKE) -C Documentation/ clean
+	$(RM) Documentation/GIT-EXCLUDED-PROGRAMS
 ifndef NO_PERL
 	$(MAKE) -C gitweb clean
 	$(RM) -r perl/build/
@@ -3084,7 +3095,7 @@
 		git-merge-octopus | git-merge-ours | git-merge-recursive | \
 		git-merge-resolve | git-merge-subtree | \
 		git-fsck-objects | git-init-db | \
-		git-remote-* | git-stage | \
+		git-remote-* | git-stage | git-legacy-* | \
 		git-?*--?* ) continue ;; \
 		esac ; \
 		test -f "Documentation/$$v.txt" || \
@@ -3108,7 +3119,7 @@
 		    -e 's/\.txt//'; \
 	) | while read how cmd; \
 	do \
-		case " $(patsubst %$X,%,$(ALL_COMMANDS)) " in \
+		case " $(patsubst %$X,%,$(ALL_COMMANDS) $(EXCLUDED_PROGRAMS)) " in \
 		*" $$cmd "*)	;; \
 		*) echo "removed but $$how: $$cmd" ;; \
 		esac; \
diff --git a/advice.c b/advice.c
index 567209a..ce5f374 100644
--- a/advice.c
+++ b/advice.c
@@ -26,6 +26,7 @@
 int advice_waiting_for_editor = 1;
 int advice_graft_file_deprecated = 1;
 int advice_checkout_ambiguous_remote_branch_name = 1;
+int advice_nested_tag = 1;
 
 static int advice_use_color = -1;
 static char advice_colors[][COLOR_MAXLEN] = {
@@ -81,6 +82,7 @@
 	{ "waitingForEditor", &advice_waiting_for_editor },
 	{ "graftFileDeprecated", &advice_graft_file_deprecated },
 	{ "checkoutAmbiguousRemoteBranchName", &advice_checkout_ambiguous_remote_branch_name },
+	{ "nestedTag", &advice_nested_tag },
 
 	/* make this an alias for backward compatibility */
 	{ "pushNonFastForward", &advice_push_update_rejected }
diff --git a/advice.h b/advice.h
index f875f8c..e50f02c 100644
--- a/advice.h
+++ b/advice.h
@@ -26,12 +26,13 @@
 extern int advice_waiting_for_editor;
 extern int advice_graft_file_deprecated;
 extern int advice_checkout_ambiguous_remote_branch_name;
+extern int advice_nested_tag;
 
 int git_default_advice_config(const char *var, const char *value);
 __attribute__((format (printf, 1, 2)))
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
-extern void NORETURN die_resolve_conflict(const char *me);
+void NORETURN die_resolve_conflict(const char *me);
 void NORETURN die_conclude_merge(void);
 void detach_advice(const char *new_name);
 
diff --git a/archive-tar.c b/archive-tar.c
index 4aabd56..3e53aac 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -326,14 +326,15 @@
 
 static void write_global_extended_header(struct archiver_args *args)
 {
-	const unsigned char *sha1 = args->commit_sha1;
+	const struct object_id *oid = args->commit_oid;
 	struct strbuf ext_header = STRBUF_INIT;
 	struct ustar_header header;
 	unsigned int mode;
 
-	if (sha1)
+	if (oid)
 		strbuf_append_ext_header(&ext_header, "comment",
-					 sha1_to_hex(sha1), 40);
+					 oid_to_hex(oid),
+					 the_hash_algo->hexsz);
 	if (args->time > USTAR_MAX_MTIME) {
 		strbuf_append_ext_header_uint(&ext_header, "mtime",
 					      args->time);
diff --git a/archive-zip.c b/archive-zip.c
index 155ee4a..4d66b5b 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -577,7 +577,7 @@
 	write_or_die(1, &locator64, ZIP64_DIR_TRAILER_LOCATOR_SIZE);
 }
 
-static void write_zip_trailer(const unsigned char *sha1)
+static void write_zip_trailer(const struct object_id *oid)
 {
 	struct zip_dir_trailer trailer;
 	int clamped = 0;
@@ -590,14 +590,14 @@
 	copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped);
 	copy_le32(trailer.size, zip_dir.len);
 	copy_le32_clamp(trailer.offset, zip_offset, &clamped);
-	copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);
+	copy_le16(trailer.comment_length, oid ? the_hash_algo->hexsz : 0);
 
 	write_or_die(1, zip_dir.buf, zip_dir.len);
 	if (clamped)
 		write_zip64_trailer();
 	write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
-	if (sha1)
-		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
+	if (oid)
+		write_or_die(1, oid_to_hex(oid), the_hash_algo->hexsz);
 }
 
 static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
@@ -635,7 +635,7 @@
 
 	err = write_archive_entries(args, write_zip_entry);
 	if (!err)
-		write_zip_trailer(args->commit_sha1);
+		write_zip_trailer(args->commit_oid);
 
 	strbuf_release(&zip_dir);
 
diff --git a/archive.c b/archive.c
index 1f98324..53141c1 100644
--- a/archive.c
+++ b/archive.c
@@ -380,7 +380,7 @@
 		int remote)
 {
 	const char *name = argv[0];
-	const unsigned char *commit_sha1;
+	const struct object_id *commit_oid;
 	time_t archive_time;
 	struct tree *tree;
 	const struct commit *commit;
@@ -402,10 +402,10 @@
 
 	commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
 	if (commit) {
-		commit_sha1 = commit->object.oid.hash;
+		commit_oid = &commit->object.oid;
 		archive_time = commit->date;
 	} else {
-		commit_sha1 = NULL;
+		commit_oid = NULL;
 		archive_time = time(NULL);
 	}
 
@@ -415,7 +415,7 @@
 
 	if (prefix) {
 		struct object_id tree_oid;
-		unsigned int mode;
+		unsigned short mode;
 		int err;
 
 		err = get_tree_entry(&tree->object.oid, prefix, &tree_oid,
@@ -426,7 +426,7 @@
 		tree = parse_tree_indirect(&tree_oid);
 	}
 	ar_args->tree = tree;
-	ar_args->commit_sha1 = commit_sha1;
+	ar_args->commit_oid = commit_oid;
 	ar_args->commit = commit;
 	ar_args->time = archive_time;
 }
diff --git a/archive.h b/archive.h
index 21ac010..e60e3dd 100644
--- a/archive.h
+++ b/archive.h
@@ -11,7 +11,7 @@
 	const char *base;
 	size_t baselen;
 	struct tree *tree;
-	const unsigned char *commit_sha1;
+	const struct object_id *commit_oid;
 	const struct commit *commit;
 	timestamp_t time;
 	struct pathspec pathspec;
@@ -23,9 +23,9 @@
 
 /* main api */
 
-extern int write_archive(int argc, const char **argv, const char *prefix,
-			 struct repository *repo,
-			 const char *name_hint, int remote);
+int write_archive(int argc, const char **argv, const char *prefix,
+		  struct repository *repo,
+		  const char *name_hint, int remote);
 
 const char *archive_format_from_filename(const char *filename);
 
@@ -39,21 +39,21 @@
 	unsigned flags;
 	void *data;
 };
-extern void register_archiver(struct archiver *);
+void register_archiver(struct archiver *);
 
-extern void init_tar_archiver(void);
-extern void init_zip_archiver(void);
-extern void init_archivers(void);
+void init_tar_archiver(void);
+void init_zip_archiver(void);
+void init_archivers(void);
 
 typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
 					const struct object_id *oid,
 					const char *path, size_t pathlen,
 					unsigned int mode);
 
-extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
-extern void *object_file_to_archive(const struct archiver_args *args,
-				    const char *path, const struct object_id *oid,
-				    unsigned int mode, enum object_type *type,
-				    unsigned long *sizep);
+int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
+void *object_file_to_archive(const struct archiver_args *args,
+			     const char *path, const struct object_id *oid,
+			     unsigned int mode, enum object_type *type,
+			     unsigned long *sizep);
 
 #endif	/* ARCHIVE_H */
diff --git a/bisect.h b/bisect.h
index 821d16e..4e69a11 100644
--- a/bisect.h
+++ b/bisect.h
@@ -11,14 +11,14 @@
  * Otherwise, it will be either all non-SAMETREE commits or the single
  * best commit, as chosen by `find_all`.
  */
-extern void find_bisection(struct commit_list **list, int *reaches, int *all,
-			   int find_all);
+void find_bisection(struct commit_list **list, int *reaches, int *all,
+		    int find_all);
 
-extern struct commit_list *filter_skipped(struct commit_list *list,
-					  struct commit_list **tried,
-					  int show_all,
-					  int *count,
-					  int *skipped_first);
+struct commit_list *filter_skipped(struct commit_list *list,
+				   struct commit_list **tried,
+				   int show_all,
+				   int *count,
+				   int *skipped_first);
 
 #define BISECT_SHOW_ALL		(1<<0)
 #define REV_LIST_QUIET		(1<<1)
@@ -31,14 +31,14 @@
 	const char *header_prefix;
 };
 
-extern int bisect_next_all(struct repository *r,
-			   const char *prefix,
-			   int no_checkout);
+int bisect_next_all(struct repository *r,
+		    const char *prefix,
+		    int no_checkout);
 
-extern int estimate_bisect_steps(int all);
+int estimate_bisect_steps(int all);
 
-extern void read_bisect_terms(const char **bad, const char **good);
+void read_bisect_terms(const char **bad, const char **good);
 
-extern int bisect_clean_state(void);
+int bisect_clean_state(void);
 
 #endif
diff --git a/blame.c b/blame.c
index 5c07dec..145eaf2 100644
--- a/blame.c
+++ b/blame.c
@@ -99,7 +99,7 @@
 	for (parents = work_tree->parents; parents; parents = parents->next) {
 		const struct object_id *commit_oid = &parents->item->object.oid;
 		struct object_id blob_oid;
-		unsigned mode;
+		unsigned short mode;
 
 		if (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&
 		    oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)
@@ -1562,7 +1562,8 @@
 	}
 	for (i = 0; i < num_sg; i++) {
 		if (sg_origin[i]) {
-			drop_origin_blob(sg_origin[i]);
+			if (!sg_origin[i]->suspects)
+				drop_origin_blob(sg_origin[i]);
 			blame_origin_decref(sg_origin[i]);
 		}
 	}
diff --git a/blame.h b/blame.h
index be3a895..d62f80f 100644
--- a/blame.h
+++ b/blame.h
@@ -52,7 +52,7 @@
 	struct blame_entry *suspects;
 	mmfile_t file;
 	struct object_id blob_oid;
-	unsigned mode;
+	unsigned short mode;
 	/* guilty gets set when shipping any suspects to the final
 	 * blame list instead of other commits
 	 */
@@ -177,6 +177,6 @@
 					long start, long end,
 					struct blame_origin *o);
 
-extern struct blame_origin *get_blame_suspects(struct commit *commit);
+struct blame_origin *get_blame_suspects(struct commit *commit);
 
 #endif /* BLAME_H */
diff --git a/branch.h b/branch.h
index 29c1afa..6f38db1 100644
--- a/branch.h
+++ b/branch.h
@@ -50,7 +50,7 @@
  * Return 1 if the named branch already exists; return 0 otherwise.
  * Fill ref with the full refname for the branch.
  */
-extern int validate_branchname(const char *name, struct strbuf *ref);
+int validate_branchname(const char *name, struct strbuf *ref);
 
 /*
  * Check if a branch 'name' can be created as a new branch; die otherwise.
@@ -58,7 +58,7 @@
  * Return 1 if the named branch already exists; return 0 otherwise.
  * Fill ref with the full refname for the branch.
  */
-extern int validate_new_branchname(const char *name, struct strbuf *ref, int force);
+int validate_new_branchname(const char *name, struct strbuf *ref, int force);
 
 /*
  * Remove information about the state of working on the current
@@ -72,26 +72,26 @@
  * Returns 0 on success.
  */
 #define BRANCH_CONFIG_VERBOSE 01
-extern int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
+int install_branch_config(int flag, const char *local, const char *origin, const char *remote);
 
 /*
  * Read branch description
  */
-extern int read_branch_desc(struct strbuf *, const char *branch_name);
+int read_branch_desc(struct strbuf *, const char *branch_name);
 
 /*
  * Check if a branch is checked out in the main worktree or any linked
  * worktree and die (with a message describing its checkout location) if
  * it is.
  */
-extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
+void die_if_checked_out(const char *branch, int ignore_current_worktree);
 
 /*
  * Update all per-worktree HEADs pointing at the old ref to point the new ref.
  * This will be used when renaming a branch. Returns 0 if successful, non-zero
  * otherwise.
  */
-extern int replace_each_worktree_head_symref(const char *oldref, const char *newref,
-					     const char *logmsg);
+int replace_each_worktree_head_symref(const char *oldref, const char *newref,
+				      const char *logmsg);
 
 #endif
diff --git a/builtin.h b/builtin.h
index b78ab6e..ec7e095 100644
--- a/builtin.h
+++ b/builtin.h
@@ -102,7 +102,7 @@
 #define PRUNE_PACKED_DRY_RUN 01
 #define PRUNE_PACKED_VERBOSE 02
 
-extern void prune_packed_objects(int);
+void prune_packed_objects(int);
 
 struct fmt_merge_msg_opts {
 	unsigned add_title:1,
@@ -110,8 +110,8 @@
 	int shortlog_len;
 };
 
-extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
-			 struct fmt_merge_msg_opts *);
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+		  struct fmt_merge_msg_opts *);
 
 /**
  * If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
@@ -123,132 +123,131 @@
  * You should most likely use a default of 0 or 1. "Punt" (-1) could be useful
  * to be able to fall back to some historical compatibility name.
  */
-extern void setup_auto_pager(const char *cmd, int def);
+void setup_auto_pager(const char *cmd, int def);
 
-extern int is_builtin(const char *s);
+int is_builtin(const char *s);
 
-extern int cmd_add(int argc, const char **argv, const char *prefix);
-extern int cmd_am(int argc, const char **argv, const char *prefix);
-extern int cmd_annotate(int argc, const char **argv, const char *prefix);
-extern int cmd_apply(int argc, const char **argv, const char *prefix);
-extern int cmd_archive(int argc, const char **argv, const char *prefix);
-extern int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
-extern int cmd_blame(int argc, const char **argv, const char *prefix);
-extern int cmd_branch(int argc, const char **argv, const char *prefix);
-extern int cmd_bundle(int argc, const char **argv, const char *prefix);
-extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
-extern int cmd_checkout(int argc, const char **argv, const char *prefix);
-extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
-extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
-extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
-extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
-extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
-extern int cmd_cherry(int argc, const char **argv, const char *prefix);
-extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
-extern int cmd_clone(int argc, const char **argv, const char *prefix);
-extern int cmd_clean(int argc, const char **argv, const char *prefix);
-extern int cmd_column(int argc, const char **argv, const char *prefix);
-extern int cmd_commit(int argc, const char **argv, const char *prefix);
-extern int cmd_commit_graph(int argc, const char **argv, const char *prefix);
-extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_config(int argc, const char **argv, const char *prefix);
-extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_credential(int argc, const char **argv, const char *prefix);
-extern int cmd_describe(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
-extern int cmd_diff(int argc, const char **argv, const char *prefix);
-extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_difftool(int argc, const char **argv, const char *prefix);
-extern int cmd_fast_export(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch(int argc, const char **argv, const char *prefix);
-extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
-extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
-extern int cmd_fsck(int argc, const char **argv, const char *prefix);
-extern int cmd_gc(int argc, const char **argv, const char *prefix);
-extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
-extern int cmd_grep(int argc, const char **argv, const char *prefix);
-extern int cmd_hash_object(int argc, const char **argv, const char *prefix);
-extern int cmd_help(int argc, const char **argv, const char *prefix);
-extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_init_db(int argc, const char **argv, const char *prefix);
-extern int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
-extern int cmd_log(int argc, const char **argv, const char *prefix);
-extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
-extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
-extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
-extern int cmd_merge(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_index(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
-extern int cmd_merge_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_mktag(int argc, const char **argv, const char *prefix);
-extern int cmd_mktree(int argc, const char **argv, const char *prefix);
-extern int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
-extern int cmd_mv(int argc, const char **argv, const char *prefix);
-extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
-extern int cmd_notes(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
-extern int cmd_patch_id(int argc, const char **argv, const char *prefix);
-extern int cmd_prune(int argc, const char **argv, const char *prefix);
-extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
-extern int cmd_pull(int argc, const char **argv, const char *prefix);
-extern int cmd_push(int argc, const char **argv, const char *prefix);
-extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
-extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_rebase(int argc, const char **argv, const char *prefix);
-extern int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
-extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_reflog(int argc, const char **argv, const char *prefix);
-extern int cmd_remote(int argc, const char **argv, const char *prefix);
-extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
-extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
-extern int cmd_repack(int argc, const char **argv, const char *prefix);
-extern int cmd_rerere(int argc, const char **argv, const char *prefix);
-extern int cmd_reset(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
-extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
-extern int cmd_revert(int argc, const char **argv, const char *prefix);
-extern int cmd_rm(int argc, const char **argv, const char *prefix);
-extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_serve(int argc, const char **argv, const char *prefix);
-extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
-extern int cmd_show(int argc, const char **argv, const char *prefix);
-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
-extern int cmd_show_index(int argc, const char **argv, const char *prefix);
-extern int cmd_status(int argc, const char **argv, const char *prefix);
-extern int cmd_stash(int argc, const char **argv, const char *prefix);
-extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
-extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
-extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_tag(int argc, const char **argv, const char *prefix);
-extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_unpack_file(int argc, const char **argv, const char *prefix);
-extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_update_index(int argc, const char **argv, const char *prefix);
-extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
-extern int cmd_upload_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_var(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_commit(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
-extern int cmd_version(int argc, const char **argv, const char *prefix);
-extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
-extern int cmd_worktree(int argc, const char **argv, const char *prefix);
-extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
-extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
-extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
-extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
-extern int cmd_replace(int argc, const char **argv, const char *prefix);
+int cmd_add(int argc, const char **argv, const char *prefix);
+int cmd_am(int argc, const char **argv, const char *prefix);
+int cmd_annotate(int argc, const char **argv, const char *prefix);
+int cmd_apply(int argc, const char **argv, const char *prefix);
+int cmd_archive(int argc, const char **argv, const char *prefix);
+int cmd_bisect__helper(int argc, const char **argv, const char *prefix);
+int cmd_blame(int argc, const char **argv, const char *prefix);
+int cmd_branch(int argc, const char **argv, const char *prefix);
+int cmd_bundle(int argc, const char **argv, const char *prefix);
+int cmd_cat_file(int argc, const char **argv, const char *prefix);
+int cmd_checkout(int argc, const char **argv, const char *prefix);
+int cmd_checkout_index(int argc, const char **argv, const char *prefix);
+int cmd_check_attr(int argc, const char **argv, const char *prefix);
+int cmd_check_ignore(int argc, const char **argv, const char *prefix);
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+int cmd_cherry(int argc, const char **argv, const char *prefix);
+int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+int cmd_clone(int argc, const char **argv, const char *prefix);
+int cmd_clean(int argc, const char **argv, const char *prefix);
+int cmd_column(int argc, const char **argv, const char *prefix);
+int cmd_commit(int argc, const char **argv, const char *prefix);
+int cmd_commit_graph(int argc, const char **argv, const char *prefix);
+int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+int cmd_config(int argc, const char **argv, const char *prefix);
+int cmd_count_objects(int argc, const char **argv, const char *prefix);
+int cmd_credential(int argc, const char **argv, const char *prefix);
+int cmd_describe(int argc, const char **argv, const char *prefix);
+int cmd_diff_files(int argc, const char **argv, const char *prefix);
+int cmd_diff_index(int argc, const char **argv, const char *prefix);
+int cmd_diff(int argc, const char **argv, const char *prefix);
+int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+int cmd_difftool(int argc, const char **argv, const char *prefix);
+int cmd_fast_export(int argc, const char **argv, const char *prefix);
+int cmd_fetch(int argc, const char **argv, const char *prefix);
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix);
+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
+int cmd_format_patch(int argc, const char **argv, const char *prefix);
+int cmd_fsck(int argc, const char **argv, const char *prefix);
+int cmd_gc(int argc, const char **argv, const char *prefix);
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
+int cmd_grep(int argc, const char **argv, const char *prefix);
+int cmd_hash_object(int argc, const char **argv, const char *prefix);
+int cmd_help(int argc, const char **argv, const char *prefix);
+int cmd_index_pack(int argc, const char **argv, const char *prefix);
+int cmd_init_db(int argc, const char **argv, const char *prefix);
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
+int cmd_log(int argc, const char **argv, const char *prefix);
+int cmd_log_reflog(int argc, const char **argv, const char *prefix);
+int cmd_ls_files(int argc, const char **argv, const char *prefix);
+int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+int cmd_ls_remote(int argc, const char **argv, const char *prefix);
+int cmd_mailinfo(int argc, const char **argv, const char *prefix);
+int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+int cmd_merge(int argc, const char **argv, const char *prefix);
+int cmd_merge_base(int argc, const char **argv, const char *prefix);
+int cmd_merge_index(int argc, const char **argv, const char *prefix);
+int cmd_merge_ours(int argc, const char **argv, const char *prefix);
+int cmd_merge_file(int argc, const char **argv, const char *prefix);
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix);
+int cmd_merge_tree(int argc, const char **argv, const char *prefix);
+int cmd_mktag(int argc, const char **argv, const char *prefix);
+int cmd_mktree(int argc, const char **argv, const char *prefix);
+int cmd_multi_pack_index(int argc, const char **argv, const char *prefix);
+int cmd_mv(int argc, const char **argv, const char *prefix);
+int cmd_name_rev(int argc, const char **argv, const char *prefix);
+int cmd_notes(int argc, const char **argv, const char *prefix);
+int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix);
+int cmd_patch_id(int argc, const char **argv, const char *prefix);
+int cmd_prune(int argc, const char **argv, const char *prefix);
+int cmd_prune_packed(int argc, const char **argv, const char *prefix);
+int cmd_pull(int argc, const char **argv, const char *prefix);
+int cmd_push(int argc, const char **argv, const char *prefix);
+int cmd_range_diff(int argc, const char **argv, const char *prefix);
+int cmd_read_tree(int argc, const char **argv, const char *prefix);
+int cmd_rebase(int argc, const char **argv, const char *prefix);
+int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
+int cmd_receive_pack(int argc, const char **argv, const char *prefix);
+int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_remote(int argc, const char **argv, const char *prefix);
+int cmd_remote_ext(int argc, const char **argv, const char *prefix);
+int cmd_remote_fd(int argc, const char **argv, const char *prefix);
+int cmd_repack(int argc, const char **argv, const char *prefix);
+int cmd_rerere(int argc, const char **argv, const char *prefix);
+int cmd_reset(int argc, const char **argv, const char *prefix);
+int cmd_rev_list(int argc, const char **argv, const char *prefix);
+int cmd_rev_parse(int argc, const char **argv, const char *prefix);
+int cmd_revert(int argc, const char **argv, const char *prefix);
+int cmd_rm(int argc, const char **argv, const char *prefix);
+int cmd_send_pack(int argc, const char **argv, const char *prefix);
+int cmd_shortlog(int argc, const char **argv, const char *prefix);
+int cmd_show(int argc, const char **argv, const char *prefix);
+int cmd_show_branch(int argc, const char **argv, const char *prefix);
+int cmd_show_index(int argc, const char **argv, const char *prefix);
+int cmd_status(int argc, const char **argv, const char *prefix);
+int cmd_stash(int argc, const char **argv, const char *prefix);
+int cmd_stripspace(int argc, const char **argv, const char *prefix);
+int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
+int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
+int cmd_tag(int argc, const char **argv, const char *prefix);
+int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+int cmd_unpack_file(int argc, const char **argv, const char *prefix);
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
+int cmd_update_index(int argc, const char **argv, const char *prefix);
+int cmd_update_ref(int argc, const char **argv, const char *prefix);
+int cmd_update_server_info(int argc, const char **argv, const char *prefix);
+int cmd_upload_archive(int argc, const char **argv, const char *prefix);
+int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix);
+int cmd_upload_pack(int argc, const char **argv, const char *prefix);
+int cmd_var(int argc, const char **argv, const char *prefix);
+int cmd_verify_commit(int argc, const char **argv, const char *prefix);
+int cmd_verify_tag(int argc, const char **argv, const char *prefix);
+int cmd_version(int argc, const char **argv, const char *prefix);
+int cmd_whatchanged(int argc, const char **argv, const char *prefix);
+int cmd_worktree(int argc, const char **argv, const char *prefix);
+int cmd_write_tree(int argc, const char **argv, const char *prefix);
+int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+int cmd_show_ref(int argc, const char **argv, const char *prefix);
+int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+int cmd_replace(int argc, const char **argv, const char *prefix);
 
 #endif
diff --git a/builtin/add.c b/builtin/add.c
index db2dfa4..dd18e5c 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -374,11 +374,12 @@
 	}
 
 	for (i = 0; i < dir->nr; i++) {
-		check_embedded_repo(dir->entries[i]->name);
 		if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
 			if (!ignore_add_errors)
 				die(_("adding files failed"));
 			exit_status = 1;
+		} else {
+			check_embedded_repo(dir->entries[i]->name);
 		}
 	}
 	return exit_status;
diff --git a/builtin/am.c b/builtin/am.c
index 4fb107a..912d982 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -486,23 +486,24 @@
 
 	while (!strbuf_getline_lf(&sb, fp)) {
 		struct object_id from_obj, to_obj;
+		const char *p;
 
-		if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
+		if (sb.len != the_hash_algo->hexsz * 2 + 1) {
 			ret = error(invalid_line, sb.buf);
 			goto finish;
 		}
 
-		if (get_oid_hex(sb.buf, &from_obj)) {
+		if (parse_oid_hex(sb.buf, &from_obj, &p)) {
 			ret = error(invalid_line, sb.buf);
 			goto finish;
 		}
 
-		if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
+		if (*p != ' ') {
 			ret = error(invalid_line, sb.buf);
 			goto finish;
 		}
 
-		if (get_oid_hex(sb.buf + GIT_SHA1_HEXSZ + 1, &to_obj)) {
+		if (get_oid_hex(p + 1, &to_obj)) {
 			ret = error(invalid_line, sb.buf);
 			goto finish;
 		}
@@ -1501,11 +1502,11 @@
 		 * review them with extra care to spot mismerges.
 		 */
 		struct rev_info rev_info;
-		const char *diff_filter_str = "--diff-filter=AM";
 
 		repo_init_revisions(the_repository, &rev_info, NULL);
 		rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
-		diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
+		rev_info.diffopt.filter |= diff_filter_bit('A');
+		rev_info.diffopt.filter |= diff_filter_bit('M');
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
diff --git a/builtin/blame.c b/builtin/blame.c
index 177c102..21cde57 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -27,6 +27,7 @@
 #include "object-store.h"
 #include "blame.h"
 #include "string-list.h"
+#include "refs.h"
 
 static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
 
@@ -993,6 +994,18 @@
 
 	revs.disable_stdin = 1;
 	setup_revisions(argc, argv, &revs, NULL);
+	if (!revs.pending.nr && is_bare_repository()) {
+		struct commit *head_commit;
+		struct object_id head_oid;
+
+		if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
+					&head_oid, NULL) ||
+		    !(head_commit = lookup_commit_reference_gently(revs.repo,
+							     &head_oid, 1)))
+			die("no such ref: HEAD");
+
+		add_pending_object(&revs, &head_commit->object, "HEAD");
+	}
 
 	init_scoreboard(&sb);
 	sb.revs = &revs;
diff --git a/builtin/branch.c b/builtin/branch.c
index 4c83055..d4359b3 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -644,8 +644,7 @@
 		OPT_MERGED(&filter, N_("print only branches that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
 		OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
-		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
-			     N_("field name to sort on"), &parse_opt_ref_sorting),
+		OPT_REF_SORT(sorting_tail),
 		{
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 			N_("print only branches of the object"), 0, parse_opt_object_name
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f95e797..ffa776c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -376,7 +376,7 @@
 							ps_matched,
 							opts);
 
-	if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
+	if (report_path_error(ps_matched, &opts->pathspec)) {
 		free(ps_matched);
 		return 1;
 	}
@@ -700,7 +700,7 @@
 		topts.initial_checkout = is_cache_unborn();
 		topts.update = 1;
 		topts.merge = 1;
-		topts.gently = opts->merge && old_branch_info->commit;
+		topts.quiet = opts->merge && old_branch_info->commit;
 		topts.verbose_update = opts->show_progress;
 		topts.fn = twoway_merge;
 		if (opts->overwrite_ignore) {
@@ -725,6 +725,7 @@
 			 */
 			struct tree *result;
 			struct tree *work;
+			struct tree *old_tree;
 			struct merge_options o;
 			struct strbuf sb = STRBUF_INIT;
 
@@ -737,6 +738,12 @@
 			 */
 			if (!old_branch_info->commit)
 				return 1;
+			old_tree = get_commit_tree(old_branch_info->commit);
+
+			if (repo_index_has_changes(the_repository, old_tree, &sb))
+				die(_("cannot continue with staged changes in "
+				      "the following files:\n%s"), sb.buf);
+			strbuf_release(&sb);
 
 			if (repo_index_has_changes(the_repository,
 						   get_commit_tree(old_branch_info->commit),
@@ -781,7 +788,7 @@
 			ret = merge_trees(&o,
 					  get_commit_tree(new_branch_info->commit),
 					  work,
-					  get_commit_tree(old_branch_info->commit),
+					  old_tree,
 					  &result);
 			if (ret < 0)
 				exit(128);
diff --git a/builtin/clone.c b/builtin/clone.c
index 50bde99..ffdd94e 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -66,6 +66,7 @@
 static int max_jobs = -1;
 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
 static struct list_objects_filter_options filter_options;
+static struct string_list server_options = STRING_LIST_INIT_NODUP;
 
 static int recurse_submodules_cb(const struct option *opt,
 				 const char *arg, int unset)
@@ -137,6 +138,8 @@
 		   N_("separate git dir from working tree")),
 	OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
 			N_("set config inside the new repository")),
+	OPT_STRING_LIST(0, "server-option", &server_options,
+			N_("server-specific"), N_("option to transmit")),
 	OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
 			TRANSPORT_FAMILY_IPV4),
 	OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -657,7 +660,8 @@
 			       const char *branch_top,
 			       const char *msg,
 			       struct transport *transport,
-			       int check_connectivity)
+			       int check_connectivity,
+			       int check_refs_only)
 {
 	const struct ref *rm = mapped_refs;
 
@@ -666,6 +670,7 @@
 
 		opt.transport = transport;
 		opt.progress = transport->progress;
+		opt.check_refs_only = !!check_refs_only;
 
 		if (check_connected(iterate_ref_map, &rm, &opt))
 			die(_("remote did not send all necessary objects"));
@@ -1136,6 +1141,9 @@
 		transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 				     option_upload_pack);
 
+	if (server_options.nr)
+		transport->server_options = &server_options;
+
 	if (filter_options.choice) {
 		struct strbuf expanded_filter_spec = STRBUF_INIT;
 		expand_list_objects_filter_spec(&filter_options,
@@ -1224,7 +1232,7 @@
 
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
 			   branch_top.buf, reflog_msg.buf, transport,
-			   !is_local);
+			   !is_local, filter_options.choice);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 4ae5027..537fdfd 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -42,6 +42,9 @@
 {
 	struct commit_graph *graph = NULL;
 	char *graph_name;
+	int open_ok;
+	int fd;
+	struct stat st;
 
 	static struct option builtin_commit_graph_verify_options[] = {
 		OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -58,11 +61,16 @@
 		opts.obj_dir = get_object_directory();
 
 	graph_name = get_commit_graph_filename(opts.obj_dir);
-	graph = load_commit_graph_one(graph_name);
+	open_ok = open_commit_graph(graph_name, &fd, &st);
+	if (!open_ok && errno == ENOENT)
+		return 0;
+	if (!open_ok)
+		die_errno(_("Could not open commit-graph '%s'"), graph_name);
+	graph = load_commit_graph_one_fd_st(fd, &st);
 	FREE_AND_NULL(graph_name);
 
 	if (!graph)
-		return 0;
+		return 1;
 
 	UNLEAK(graph);
 	return verify_commit_graph(the_repository, graph);
@@ -72,6 +80,9 @@
 {
 	struct commit_graph *graph = NULL;
 	char *graph_name;
+	int open_ok;
+	int fd;
+	struct stat st;
 
 	static struct option builtin_commit_graph_read_options[] = {
 		OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -88,10 +99,14 @@
 		opts.obj_dir = get_object_directory();
 
 	graph_name = get_commit_graph_filename(opts.obj_dir);
-	graph = load_commit_graph_one(graph_name);
 
+	open_ok = open_commit_graph(graph_name, &fd, &st);
+	if (!open_ok)
+		die_errno(_("Could not open commit-graph '%s'"), graph_name);
+
+	graph = load_commit_graph_one_fd_st(fd, &st);
 	if (!graph)
-		die("graph file %s does not exist", graph_name);
+		return 1;
 
 	FREE_AND_NULL(graph_name);
 
diff --git a/builtin/commit.c b/builtin/commit.c
index f175374..bd4b180 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -235,7 +235,7 @@
  * and return the paths that match the given pattern in list.
  */
 static int list_paths(struct string_list *list, const char *with_tree,
-		      const char *prefix, const struct pathspec *pattern)
+		      const struct pathspec *pattern)
 {
 	int i, ret;
 	char *m;
@@ -264,7 +264,7 @@
 			item->util = item; /* better a valid pointer than a fake one */
 	}
 
-	ret = report_path_error(m, pattern, prefix);
+	ret = report_path_error(m, pattern);
 	free(m);
 	return ret;
 }
@@ -454,7 +454,7 @@
 			die(_("cannot do a partial commit during a cherry-pick."));
 	}
 
-	if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
+	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
 		exit(1);
 
 	discard_cache();
@@ -668,6 +668,7 @@
 	const char *hook_arg2 = NULL;
 	int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
 	int old_display_comment_prefix;
+	int merge_contains_scissors = 0;
 
 	/* This checks and barfs if author is badly specified */
 	determine_author_info(author_ident);
@@ -728,6 +729,8 @@
 			strbuf_addbuf(&sb, &message);
 		hook_arg1 = "message";
 	} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
+		size_t merge_msg_start;
+
 		/*
 		 * prepend SQUASH_MSG here if it exists and a
 		 * "merge --squash" was originally performed
@@ -738,8 +741,16 @@
 			hook_arg1 = "squash";
 		} else
 			hook_arg1 = "merge";
+
+		merge_msg_start = sb.len;
 		if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0)
 			die_errno(_("could not read MERGE_MSG"));
+
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+		    wt_status_locate_end(sb.buf + merge_msg_start,
+					 sb.len - merge_msg_start) <
+				sb.len - merge_msg_start)
+			merge_contains_scissors = 1;
 	} else if (!stat(git_path_squash_msg(the_repository), &statbuf)) {
 		if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0)
 			die_errno(_("could not read SQUASH_MSG"));
@@ -807,7 +818,8 @@
 		struct ident_split ci, ai;
 
 		if (whence != FROM_COMMIT) {
-			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+				!merge_contains_scissors)
 				wt_status_add_cut_line(s->fp);
 			status_printf_ln(s, GIT_COLOR_NORMAL,
 			    whence == FROM_MERGE
@@ -832,10 +844,10 @@
 				_("Please enter the commit message for your changes."
 				  " Lines starting\nwith '%c' will be ignored, and an empty"
 				  " message aborts the commit.\n"), comment_line_char);
-		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
-			 whence == FROM_COMMIT)
-			wt_status_add_cut_line(s->fp);
-		else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
+		else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+			if (whence == FROM_COMMIT && !merge_contains_scissors)
+				wt_status_add_cut_line(s->fp);
+		} else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
 			status_printf(s, GIT_COLOR_NORMAL,
 				_("Please enter the commit message for your changes."
 				  " Lines starting\n"
@@ -1172,29 +1184,13 @@
 		die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
 	if (argc == 0 && (also || (only && !amend && !allow_empty)))
 		die(_("No paths with --include/--only does not make sense."));
-	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
-					    COMMIT_MSG_CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "verbatim"))
-		cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
-	else if (!strcmp(cleanup_arg, "whitespace"))
-		cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "strip"))
-		cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
-	else if (!strcmp(cleanup_arg, "scissors"))
-		cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
-					    COMMIT_MSG_CLEANUP_SPACE;
-	/*
-	 * Please update _git_commit() in git-completion.bash when you
-	 * add new options.
-	 */
-	else
-		die(_("Invalid cleanup mode %s"), cleanup_arg);
+	cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
 
 	handle_untracked_files_arg(s);
 
 	if (all && argc > 0)
-		die(_("Paths with -a does not make sense."));
+		die(_("paths '%s ...' with -a does not make sense"),
+		    argv[0]);
 
 	if (status_format != STATUS_FORMAT_NONE)
 		dry_run = 1;
@@ -1490,7 +1486,7 @@
 		OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
-		OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
+		OPT_CLEANUP(&cleanup_arg),
 		OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
 		{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 		  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
@@ -1626,11 +1622,7 @@
 		die(_("could not read commit message: %s"), strerror(saved_errno));
 	}
 
-	if (verbose || /* Truncate the message just before the diff, if any. */
-	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
-		strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
-		strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+	cleanup_message(&sb, cleanup_mode, verbose);
 
 	if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
 		rollback_index_files();
diff --git a/builtin/diff.c b/builtin/diff.c
index 53d4234..42ac803 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -321,24 +321,6 @@
 
 	repo_init_revisions(the_repository, &rev, prefix);
 
-	if (no_index && argc != i + 2) {
-		if (no_index == DIFF_NO_INDEX_IMPLICIT) {
-			/*
-			 * There was no --no-index and there were not two
-			 * paths. It is possible that the user intended
-			 * to do an inside-repository operation.
-			 */
-			fprintf(stderr, "Not a git repository\n");
-			fprintf(stderr,
-				"To compare two paths outside a working tree:\n");
-		}
-		/* Give the usage message for non-repository usage and exit. */
-		usagef("git diff %s <path> <path>",
-		       no_index == DIFF_NO_INDEX_EXPLICIT ?
-		       "--no-index" : "[--no-index]");
-
-	}
-
 	/* Set up defaults that will apply to both no-index and regular diffs. */
 	rev.diffopt.stat_width = -1;
 	rev.diffopt.stat_graph_width = -1;
@@ -347,7 +329,9 @@
 
 	/* If this is a no-index diff, just run it and exit there. */
 	if (no_index)
-		diff_no_index(&rev, argc, argv);
+		exit(diff_no_index(&rev, no_index == DIFF_NO_INDEX_IMPLICIT,
+				   argc, argv));
+
 
 	/*
 	 * Otherwise, we are doing the usual "git" diff; set up any
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a3ea60e..04ffa1d 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -65,14 +65,12 @@
 	*mode2 = (int)strtol(p + 1, &p, 8);
 	if (*p != ' ')
 		return error("expected ' ', got '%c'", *p);
-	if (get_oid_hex(++p, oid1))
-		return error("expected object ID, got '%s'", p + 1);
-	p += GIT_SHA1_HEXSZ;
+	if (parse_oid_hex(++p, oid1, (const char **)&p))
+		return error("expected object ID, got '%s'", p);
 	if (*p != ' ')
 		return error("expected ' ', got '%c'", *p);
-	if (get_oid_hex(++p, oid2))
-		return error("expected object ID, got '%s'", p + 1);
-	p += GIT_SHA1_HEXSZ;
+	if (parse_oid_hex(++p, oid2, (const char **)&p))
+		return error("expected object ID, got '%s'", p);
 	if (*p != ' ')
 		return error("expected ' ', got '%c'", *p);
 	*status = *++p;
@@ -690,7 +688,7 @@
 int cmd_difftool(int argc, const char **argv, const char *prefix)
 {
 	int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
-	    tool_help = 0;
+	    tool_help = 0, no_index = 0;
 	static char *difftool_cmd = NULL, *extcmd = NULL;
 	struct option builtin_difftool_options[] = {
 		OPT_BOOL('g', "gui", &use_gui_tool,
@@ -714,6 +712,7 @@
 			    "tool returns a non - zero exit code")),
 		OPT_STRING('x', "extcmd", &extcmd, N_("command"),
 			   N_("specify a custom command for viewing diffs")),
+		OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
 		OPT_END()
 	};
 
@@ -727,9 +726,14 @@
 	if (tool_help)
 		return print_tool_help();
 
-	/* NEEDSWORK: once we no longer spawn anything, remove this */
-	setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
-	setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
+	if (!no_index && !startup_info->have_repository)
+		die(_("difftool requires worktree or --no-index"));
+
+	if (!no_index){
+		setup_work_tree();
+		setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
+		setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
+	}
 
 	if (use_gui_tool && diff_gui_tool && *diff_gui_tool)
 		setenv("GIT_DIFF_TOOL", diff_gui_tool, 1);
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 153a2bd..dc1485c 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -234,7 +234,7 @@
 		BUG("unknown protocol version");
 	}
 
-	ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
+	ref = fetch_pack(&args, fd, ref, sought, nr_sought,
 			 &shallow, pack_lockfile_ptr, version);
 	if (pack_lockfile) {
 		printf("lock %s\n", pack_lockfile);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e931be9..465153e 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -37,8 +37,7 @@
 		OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
 		OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
 		OPT__COLOR(&format.use_color, N_("respect format colors")),
-		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
-			    N_("field name to sort on"), &parse_opt_ref_sorting),
+		OPT_REF_SORT(sorting_tail),
 		OPT_CALLBACK(0, "points-at", &filter.points_at,
 			     N_("object"), N_("print only refs which points at the given object"),
 			     parse_opt_object_name),
diff --git a/builtin/gc.c b/builtin/gc.c
index 020f725..8943bcc 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -116,6 +116,19 @@
 	raise(signo);
 }
 
+static int gc_config_is_timestamp_never(const char *var)
+{
+	const char *value;
+	timestamp_t expire;
+
+	if (!git_config_get_value(var, &value) && value) {
+		if (parse_expiry_date(value, &expire))
+			die(_("failed to parse '%s' value '%s'"), var, value);
+		return expire == 0;
+	}
+	return 0;
+}
+
 static void gc_config(void)
 {
 	const char *value;
@@ -127,6 +140,10 @@
 			pack_refs = git_config_bool("gc.packrefs", value);
 	}
 
+	if (gc_config_is_timestamp_never("gc.reflogexpire") &&
+	    gc_config_is_timestamp_never("gc.reflogexpireunreachable"))
+		prune_reflogs = 0;
+
 	git_config_get_int("gc.aggressivewindow", &aggressive_window);
 	git_config_get_int("gc.aggressivedepth", &aggressive_depth);
 	git_config_get_int("gc.auto", &gc_auto_threshold);
@@ -156,9 +173,7 @@
 	int auto_threshold;
 	int num_loose = 0;
 	int needed = 0;
-
-	if (gc_auto_threshold <= 0)
-		return 0;
+	const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
 
 	dir = opendir(git_path("objects/17"));
 	if (!dir)
@@ -166,8 +181,8 @@
 
 	auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256);
 	while ((ent = readdir(dir)) != NULL) {
-		if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
-		    ent->d_name[38] != '\0')
+		if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose ||
+		    ent->d_name[hexsz_loose] != '\0')
 			continue;
 		if (++num_loose > auto_threshold) {
 			needed = 1;
@@ -491,14 +506,20 @@
 
 static void gc_before_repack(void)
 {
+	/*
+	 * We may be called twice, as both the pre- and
+	 * post-daemonized phases will call us, but running these
+	 * commands more than once is pointless and wasteful.
+	 */
+	static int done = 0;
+	if (done++)
+		return;
+
 	if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
 		die(FAILED_RUN, pack_refs_cmd.argv[0]);
 
 	if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
 		die(FAILED_RUN, reflog.argv[0]);
-
-	pack_refs = 0;
-	prune_reflogs = 0;
 }
 
 int cmd_gc(int argc, const char **argv, const char *prefix)
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 2706fcf..491af92 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -21,6 +21,8 @@
 	char *content = buffer + RECORDSIZE;
 	const char *comment;
 	ssize_t n;
+	long len;
+	char *end;
 
 	if (argc != 1)
 		usage(builtin_get_tar_commit_id_usage);
@@ -32,10 +34,18 @@
 		die_errno("git get-tar-commit-id: EOF before reading tar header");
 	if (header->typeflag[0] != 'g')
 		return 1;
-	if (!skip_prefix(content, "52 comment=", &comment))
+
+	len = strtol(content, &end, 10);
+	if (errno == ERANGE || end == content || len < 0)
+		return 1;
+	if (!skip_prefix(end, " comment=", &comment))
+		return 1;
+	len -= comment - content;
+	if (len < 1 || !(len % 2) ||
+	    hash_algo_by_length((len - 1) / 2) == GIT_HASH_UNKNOWN)
 		return 1;
 
-	if (write_in_full(1, comment, 41) < 0)
+	if (write_in_full(1, comment, len) < 0)
 		die_errno("git get-tar-commit-id: write error");
 
 	return 0;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 31046c7..ccf4eb7 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -219,8 +219,16 @@
 	unsigned i, max, foreign_nr = 0;
 
 	max = get_max_object_index();
-	for (i = 0; i < max; i++)
+
+	if (verbose)
+		progress = start_delayed_progress(_("Checking objects"), max);
+
+	for (i = 0; i < max; i++) {
 		foreign_nr += check_object(get_indexed_object(i));
+		display_progress(progress, i + 1);
+	}
+
+	stop_progress(&progress);
 	return foreign_nr;
 }
 
diff --git a/builtin/log.c b/builtin/log.c
index e63c8c2..e43ee12 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -251,7 +251,7 @@
  * This gives a rough estimate for how many commits we
  * will print out in the list.
  */
-static int estimate_commit_count(struct rev_info *rev, struct commit_list *list)
+static int estimate_commit_count(struct commit_list *list)
 {
 	int n = 0;
 
@@ -289,7 +289,7 @@
 		switch (simplify_commit(revs, commit)) {
 		case commit_show:
 			if (show_header) {
-				int n = estimate_commit_count(revs, list);
+				int n = estimate_commit_count(list);
 				show_early_header(revs, "incomplete", n);
 				show_header = 0;
 			}
@@ -333,7 +333,7 @@
 	show_early_output = log_show_early;
 }
 
-static void setup_early_output(struct rev_info *rev)
+static void setup_early_output(void)
 {
 	struct sigaction sa;
 
@@ -364,7 +364,7 @@
 
 static void finish_early_output(struct rev_info *rev)
 {
-	int n = estimate_commit_count(rev, rev->commits);
+	int n = estimate_commit_count(rev->commits);
 	signal(SIGALRM, SIG_IGN);
 	show_early_header(rev, "done", n);
 }
@@ -376,7 +376,7 @@
 	int saved_dcctc = 0, close_file = rev->diffopt.close_file;
 
 	if (rev->early_output)
-		setup_early_output(rev);
+		setup_early_output();
 
 	if (prepare_revision_walk(rev))
 		die(_("revision walk setup failed"));
@@ -490,7 +490,7 @@
 	return cmd_log_walk(&rev);
 }
 
-static void show_tagger(char *buf, int len, struct rev_info *rev)
+static void show_tagger(const char *buf, struct rev_info *rev)
 {
 	struct strbuf out = STRBUF_INIT;
 	struct pretty_print_context pp = {0};
@@ -546,11 +546,11 @@
 	assert(type == OBJ_TAG);
 	while (offset < size && buf[offset] != '\n') {
 		int new_offset = offset + 1;
+		const char *ident;
 		while (new_offset < size && buf[new_offset++] != '\n')
 			; /* do nothing */
-		if (starts_with(buf + offset, "tagger "))
-			show_tagger(buf + offset + 7,
-				    new_offset - offset - 7, rev);
+		if (skip_prefix(buf + offset, "tagger ", &ident))
+			show_tagger(ident, rev);
 		offset = new_offset;
 	}
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 29a8762..7f83c9a 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -112,11 +112,11 @@
 	if (debug_mode) {
 		const struct stat_data *sd = &ce->ce_stat_data;
 
-		printf("  ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
-		printf("  mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
-		printf("  dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
-		printf("  uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
-		printf("  size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
+		printf("  ctime: %u:%u\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+		printf("  mtime: %u:%u\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+		printf("  dev: %u\tino: %u\n", sd->sd_dev, sd->sd_ino);
+		printf("  uid: %u\tgid: %u\n", sd->sd_uid, sd->sd_gid);
+		printf("  size: %u\tflags: %x\n", sd->sd_size, ce->ce_flags);
 	}
 }
 
@@ -680,7 +680,7 @@
 
 	if (ps_matched) {
 		int bad;
-		bad = report_path_error(ps_matched, &pathspec, prefix);
+		bad = report_path_error(ps_matched, &pathspec);
 		if (bad)
 			fprintf(stderr, "Did you forget to 'git add'?\n");
 
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 1d7f1f5..6ef5195 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -67,8 +67,7 @@
 		OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
 		OPT_BOOL(0, "get-url", &get_url,
 			 N_("take url.<base>.insteadOf into account")),
-		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
-			     N_("field name to sort on"), &parse_opt_ref_sorting),
+		OPT_REF_SORT(sorting_tail),
 		OPT_SET_INT_F(0, "exit-code", &status,
 			      N_("exit with exit code 2 if no matching refs are found"),
 			      2, PARSE_OPT_NOCOMPLETE),
diff --git a/builtin/merge.c b/builtin/merge.c
index 5ce8946..e96f72a 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -38,6 +38,7 @@
 #include "tag.h"
 #include "alias.h"
 #include "commit-reach.h"
+#include "wt-status.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -98,6 +99,9 @@
 
 static enum ff_type fast_forward = FF_ALLOW;
 
+static const char *cleanup_arg;
+static enum commit_msg_cleanup_mode cleanup_mode;
+
 static int option_parse_message(const struct option *opt,
 				const char *arg, int unset)
 {
@@ -249,6 +253,7 @@
 		N_("perform a commit if the merge succeeds (default)")),
 	OPT_BOOL('e', "edit", &option_edit,
 		N_("edit message before committing")),
+	OPT_CLEANUP(&cleanup_arg),
 	OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
 	OPT_SET_INT_F(0, "ff-only", &fast_forward,
 		      N_("abort if fast-forward is not possible"),
@@ -612,6 +617,8 @@
 		return git_config_string(&pull_twohead, k, v);
 	else if (!strcmp(k, "pull.octopus"))
 		return git_config_string(&pull_octopus, k, v);
+	else if (!strcmp(k, "commit.cleanup"))
+		return git_config_string(&cleanup_arg, k, v);
 	else if (!strcmp(k, "merge.renormalize"))
 		option_renormalize = git_config_bool(k, v);
 	else if (!strcmp(k, "merge.ff")) {
@@ -800,8 +807,13 @@
 static const char merge_editor_comment[] =
 N_("Please enter a commit message to explain why this merge is necessary,\n"
    "especially if it merges an updated upstream into a topic branch.\n"
-   "\n"
-   "Lines starting with '%c' will be ignored, and an empty message aborts\n"
+   "\n");
+
+static const char scissors_editor_comment[] =
+N_("An empty message aborts the commit.\n");
+
+static const char no_scissors_editor_comment[] =
+N_("Lines starting with '%c' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
 static void write_merge_heads(struct commit_list *);
@@ -809,11 +821,19 @@
 {
 	struct strbuf msg = STRBUF_INIT;
 	strbuf_addbuf(&msg, &merge_msg);
-	strbuf_addch(&msg, '\n');
 	if (squash)
 		BUG("the control must not reach here under --squash");
-	if (0 < option_edit)
-		strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+	if (0 < option_edit) {
+		strbuf_addch(&msg, '\n');
+		if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+			wt_status_append_cut_line(&msg);
+			strbuf_commented_addf(&msg, "\n");
+		}
+		strbuf_commented_addf(&msg, _(merge_editor_comment));
+		strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
+			scissors_editor_comment :
+			no_scissors_editor_comment), comment_line_char);
+	}
 	if (signoff)
 		append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
 	write_merge_heads(remoteheads);
@@ -832,7 +852,7 @@
 		abort_commit(remoteheads, NULL);
 
 	read_merge_msg(&msg);
-	strbuf_stripspace(&msg, 0 < option_edit);
+	cleanup_message(&msg, cleanup_mode, 0);
 	if (!msg.len)
 		abort_commit(remoteheads, _("Empty commit message."));
 	strbuf_release(&merge_msg);
@@ -880,7 +900,6 @@
 	parents = remoteheads;
 	if (!head_subsumed || fast_forward == FF_NO)
 		commit_list_insert(head, &parents);
-	strbuf_addch(&merge_msg, '\n');
 	prepare_to_commit(remoteheads);
 	if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
 			&result_commit, NULL, sign_commit))
@@ -901,7 +920,15 @@
 	filename = git_path_merge_msg(the_repository);
 	fp = xfopen(filename, "a");
 
-	append_conflicts_hint(&the_index, &msgbuf);
+	/*
+	 * We can't use cleanup_mode because if we're not using the editor,
+	 * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even
+	 * though the message is meant to be processed later by git-commit.
+	 * Thus, we will get the cleanup mode which is returned when we _are_
+	 * using an editor.
+	 */
+	append_conflicts_hint(&the_index, &msgbuf,
+			      get_cleanup_mode(cleanup_arg, 1));
 	fputs(msgbuf.buf, fp);
 	strbuf_release(&msgbuf);
 	fclose(fp);
@@ -1301,6 +1328,11 @@
 	}
 	resolve_undo_clear();
 
+	if (option_edit < 0)
+		option_edit = default_edit_option();
+
+	cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit);
+
 	if (verbosity < 0)
 		show_diffstat = 0;
 
@@ -1386,9 +1418,6 @@
 			fast_forward = FF_NO;
 	}
 
-	if (option_edit < 0)
-		option_edit = default_edit_option();
-
 	if (!use_strategies) {
 		if (!remoteheads)
 			; /* already up-to-date */
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index f1cb45c..05ccf53 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -361,23 +361,25 @@
 static void name_rev_line(char *p, struct name_ref_data *data)
 {
 	struct strbuf buf = STRBUF_INIT;
-	int forty = 0;
+	int counter = 0;
 	char *p_start;
+	const unsigned hexsz = the_hash_algo->hexsz;
+
 	for (p_start = p; *p; p++) {
 #define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
 		if (!ishex(*p))
-			forty = 0;
-		else if (++forty == GIT_SHA1_HEXSZ &&
+			counter = 0;
+		else if (++counter == hexsz &&
 			 !ishex(*(p+1))) {
 			struct object_id oid;
 			const char *name = NULL;
 			char c = *(p+1);
 			int p_len = p - p_start + 1;
 
-			forty = 0;
+			counter = 0;
 
 			*(p+1) = 0;
-			if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) {
+			if (!get_oid(p - (hexsz - 1), &oid)) {
 				struct object *o =
 					lookup_object(the_repository,
 						      oid.hash);
@@ -390,7 +392,7 @@
 				continue;
 
 			if (data->name_only)
-				printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name);
+				printf("%.*s%s", p_len - hexsz, p_start, name);
 			else
 				printf("%.*s (%s)", p_len, p_start, name);
 			p_start = p + 1;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index a154fc2..9f424aa 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -97,7 +97,7 @@
 static int use_bitmap_index_default = 1;
 static int use_bitmap_index = -1;
 static int write_bitmap_index;
-static uint16_t write_bitmap_options;
+static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;
 
 static int exclude_promisor_objects;
 
@@ -964,6 +964,8 @@
 	if (written != nr_result)
 		die(_("wrote %"PRIu32" objects while expecting %"PRIu32),
 		    written, nr_result);
+	trace2_data_intmax("pack-objects", the_repository,
+			   "write_pack_file/wrote", nr_result);
 }
 
 static int no_try_delta(const char *path)
@@ -1487,6 +1489,7 @@
 			   struct object_entry **base_out)
 {
 	struct object_entry *base;
+	struct object_id base_oid;
 
 	if (!base_sha1)
 		return 0;
@@ -1508,10 +1511,9 @@
 	 * even if it was buried too deep in history to make it into the
 	 * packing list.
 	 */
-	if (thin && bitmap_has_sha1_in_uninteresting(bitmap_git, base_sha1)) {
+	oidread(&base_oid, base_sha1);
+	if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, &base_oid)) {
 		if (use_delta_islands) {
-			struct object_id base_oid;
-			hashcpy(base_oid.hash, base_sha1);
 			if (!in_same_island(&delta->idx.oid, &base_oid))
 				return 0;
 		}
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 68c1e54..178e340 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -641,7 +641,7 @@
 	pl = red = pack_list_difference(local_packs, min);
 	while (pl) {
 		printf("%s\n%s\n",
-		       sha1_pack_index_name(pl->pack->sha1),
+		       sha1_pack_index_name(pl->pack->hash),
 		       pl->pack->pack_name);
 		pl = pl->next;
 	}
diff --git a/builtin/pull.c b/builtin/pull.c
index 33db889..9dd32a1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -24,6 +24,7 @@
 #include "lockfile.h"
 #include "wt-status.h"
 #include "commit-reach.h"
+#include "sequencer.h"
 
 enum rebase_type {
 	REBASE_INVALID = -1,
@@ -101,6 +102,7 @@
 static char *opt_squash;
 static char *opt_commit;
 static char *opt_edit;
+static char *cleanup_arg;
 static char *opt_ff;
 static char *opt_verify_signatures;
 static int opt_autostash = -1;
@@ -168,6 +170,7 @@
 	OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
 		N_("edit message before committing"),
 		PARSE_OPT_NOARG),
+	OPT_CLEANUP(&cleanup_arg),
 	OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
 		N_("allow fast-forward"),
 		PARSE_OPT_NOARG),
@@ -369,9 +372,10 @@
 
 	fp = xfopen(filename, "r");
 	while (strbuf_getline_lf(&sb, fp) != EOF) {
-		if (get_oid_hex(sb.buf, &oid))
-			continue;  /* invalid line: does not start with SHA1 */
-		if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
+		const char *p;
+		if (parse_oid_hex(sb.buf, &oid, &p))
+			continue;  /* invalid line: does not start with object ID */
+		if (starts_with(p, "\tnot-for-merge\t"))
 			continue;  /* ref is not-for-merge */
 		oid_array_append(merge_heads, &oid);
 	}
@@ -644,6 +648,8 @@
 		argv_array_push(&args, opt_commit);
 	if (opt_edit)
 		argv_array_push(&args, opt_edit);
+	if (cleanup_arg)
+		argv_array_pushf(&args, "--cleanup=%s", cleanup_arg);
 	if (opt_ff)
 		argv_array_push(&args, opt_ff);
 	if (opt_verify_signatures)
@@ -760,7 +766,7 @@
 	cp.no_stderr = 1;
 	cp.git_cmd = 1;
 
-	ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ);
+	ret = capture_command(&cp, &sb, GIT_MAX_HEXSZ);
 	if (ret)
 		goto cleanup;
 
@@ -805,7 +811,7 @@
 }
 
 /**
- * Given the current HEAD SHA1, the merge head returned from git-fetch and the
+ * Given the current HEAD oid, the merge head returned from git-fetch and the
  * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
  * appropriate arguments and returns its exit status.
  */
@@ -875,6 +881,13 @@
 
 	argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 
+	if (cleanup_arg)
+		/*
+		 * this only checks the validity of cleanup_arg; we don't need
+		 * a valid value for use_editor
+		 */
+		get_cleanup_mode(cleanup_arg, 0);
+
 	parse_repo_refspecs(argc, argv, &repo, &refspecs);
 
 	if (!opt_ff)
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index f01a0be..784bd19 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -16,42 +16,27 @@
 	int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
 	struct diff_options diffopt = { NULL };
 	int simple_color = -1;
-	struct option options[] = {
+	struct option range_diff_options[] = {
 		OPT_INTEGER(0, "creation-factor", &creation_factor,
 			    N_("Percentage by which creation is weighted")),
 		OPT_BOOL(0, "no-dual-color", &simple_color,
 			    N_("use simple diff colors")),
 		OPT_END()
 	};
-	int i, j, res = 0;
+	struct option *options;
+	int res = 0;
 	struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT;
 
 	git_config(git_diff_ui_config, NULL);
 
 	repo_diff_setup(the_repository, &diffopt);
 
+	options = parse_options_concat(range_diff_options, diffopt.parseopts);
 	argc = parse_options(argc, argv, NULL, options,
-			     builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN |
-			     PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
-
-	for (i = j = 1; i < argc && strcmp("--", argv[i]); ) {
-		int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix);
-
-		if (!c)
-			argv[j++] = argv[i++];
-		else
-			i += c;
-	}
-	while (i < argc)
-		argv[j++] = argv[i++];
-	argc = j;
-	diff_setup_done(&diffopt);
-
-	/* Make sure that there are no unparsed options */
-	argc = parse_options(argc, argv, NULL,
-			     options + ARRAY_SIZE(options) - 1, /* OPT_END */
 			     builtin_range_diff_usage, 0);
 
+	diff_setup_done(&diffopt);
+
 	/* force color when --dual-color was used */
 	if (!simple_color)
 		diffopt.use_color = 1;
@@ -90,6 +75,7 @@
 		error(_("need two commit ranges"));
 		usage_with_options(builtin_range_diff_usage, options);
 	}
+	FREE_AND_NULL(options);
 
 	res = show_range_diff(range1.buf, range2.buf, creation_factor,
 			      simple_color < 1, &diffopt);
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 9083dcf..5c9c082 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -154,6 +154,7 @@
 		{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
 			    "checkout", "control recursive updating of submodules",
 			    PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+		OPT__QUIET(&opts.quiet, N_("suppress feedback messages")),
 		OPT_END()
 	};
 
diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 888390f..4535523 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -14,6 +14,103 @@
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
+static int add_exec_commands(struct string_list *commands)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	todo_list_add_exec_commands(&todo_list, commands);
+	res = todo_list_write_to_file(the_repository, &todo_list,
+				      todo_file, NULL, NULL, -1, 0);
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int rearrange_squash_in_todo_file(void)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res = 0;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	res = todo_list_rearrange_squash(&todo_list);
+	if (!res)
+		res = todo_list_write_to_file(the_repository, &todo_list,
+					      todo_file, NULL, NULL, -1, 0);
+
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int transform_todo_file(unsigned flags)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT;
+	int res;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+					&todo_list)) {
+		todo_list_release(&todo_list);
+		return error(_("unusable todo list: '%s'"), todo_file);
+	}
+
+	res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
+				      NULL, NULL, -1, flags);
+	todo_list_release(&todo_list);
+
+	if (res)
+		return error_errno(_("could not write '%s'."), todo_file);
+	return 0;
+}
+
+static int edit_todo_file(unsigned flags)
+{
+	const char *todo_file = rebase_path_todo();
+	struct todo_list todo_list = TODO_LIST_INIT,
+		new_todo = TODO_LIST_INIT;
+	int res = 0;
+
+	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+		return error_errno(_("could not read '%s'."), todo_file);
+
+	strbuf_stripspace(&todo_list.buf, 1);
+	res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+	if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
+					    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
+		res = error_errno(_("could not write '%s'"), todo_file);
+
+	todo_list_release(&todo_list);
+	todo_list_release(&new_todo);
+
+	return res;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
 			       const char **head_hash,
 			       char **revisions, char **shortrevisions)
@@ -66,13 +163,13 @@
 				 const char *onto, const char *onto_name,
 				 const char *squash_onto, const char *head_name,
 				 const char *restrict_revision, char *raw_strategies,
-				 const char *cmd, unsigned autosquash)
+				 struct string_list *commands, unsigned autosquash)
 {
 	int ret;
 	const char *head_hash = NULL;
 	char *revisions = NULL, *shortrevisions = NULL;
 	struct argv_array make_script_args = ARGV_ARRAY_INIT;
-	FILE *todo_list;
+	struct todo_list todo_list = TODO_LIST_INIT;
 
 	if (prepare_branch_to_be_rebased(opts, switch_to))
 		return -1;
@@ -94,34 +191,29 @@
 	if (!upstream && squash_onto)
 		write_file(path_squash_onto(), "%s\n", squash_onto);
 
-	todo_list = fopen(rebase_path_todo(), "w");
-	if (!todo_list) {
-		free(revisions);
-		free(shortrevisions);
-
-		return error_errno(_("could not open %s"), rebase_path_todo());
-	}
-
 	argv_array_pushl(&make_script_args, "", revisions, NULL);
 	if (restrict_revision)
 		argv_array_push(&make_script_args, restrict_revision);
 
-	ret = sequencer_make_script(the_repository, todo_list,
+	ret = sequencer_make_script(the_repository, &todo_list.buf,
 				    make_script_args.argc, make_script_args.argv,
 				    flags);
-	fclose(todo_list);
 
 	if (ret)
 		error(_("could not generate todo list"));
 	else {
 		discard_cache();
-		ret = complete_action(the_repository, opts, flags,
-				      shortrevisions, onto_name, onto,
-				      head_hash, cmd, autosquash);
+		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+						&todo_list))
+			BUG("unusable todo list");
+
+		ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
+				      onto, head_hash, commands, autosquash, &todo_list);
 	}
 
 	free(revisions);
 	free(shortrevisions);
+	todo_list_release(&todo_list);
 	argv_array_clear(&make_script_args);
 
 	return ret;
@@ -140,6 +232,7 @@
 	const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
 		*squash_onto = NULL, *upstream = NULL, *head_name = NULL,
 		*switch_to = NULL, *cmd = NULL;
+	struct string_list commands = STRING_LIST_INIT_DUP;
 	char *raw_strategies = NULL;
 	enum {
 		NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
@@ -224,6 +317,14 @@
 		warning(_("--[no-]rebase-cousins has no effect without "
 			  "--rebase-merges"));
 
+	if (cmd && *cmd) {
+		string_list_split(&commands, cmd, '\n', -1);
+
+		/* rebase.c adds a new line to cmd after every command,
+		 * so here the last command is always empty */
+		string_list_remove_empty_items(&commands, 0);
+	}
+
 	switch (command) {
 	case NONE:
 		if (!onto && !upstream)
@@ -231,7 +332,7 @@
 
 		ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
 					    onto_name, squash_onto, head_name, restrict_revision,
-					    raw_strategies, cmd, autosquash);
+					    raw_strategies, &commands, autosquash);
 		break;
 	case SKIP: {
 		struct string_list merge_rr = STRING_LIST_INIT_DUP;
@@ -243,7 +344,7 @@
 		break;
 	}
 	case EDIT_TODO:
-		ret = edit_todo_list(the_repository, flags);
+		ret = edit_todo_file(flags);
 		break;
 	case SHOW_CURRENT_PATCH: {
 		struct child_process cmd = CHILD_PROCESS_INIT;
@@ -256,20 +357,21 @@
 	}
 	case SHORTEN_OIDS:
 	case EXPAND_OIDS:
-		ret = transform_todos(the_repository, flags);
+		ret = transform_todo_file(flags);
 		break;
 	case CHECK_TODO_LIST:
-		ret = check_todo_list(the_repository);
+		ret = check_todo_list_from_file(the_repository);
 		break;
 	case REARRANGE_SQUASH:
-		ret = rearrange_squash(the_repository);
+		ret = rearrange_squash_in_todo_file();
 		break;
 	case ADD_EXEC:
-		ret = sequencer_add_exec_commands(the_repository, cmd);
+		ret = add_exec_commands(&commands);
 		break;
 	default:
 		BUG("invalid command '%d'", command);
 	}
 
+	string_list_clear(&commands, 0);
 	return !!ret;
 }
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 2e41ad5..966b953 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1568,8 +1568,7 @@
 				branch_name = options.head_name;
 
 		} else {
-			free(options.head_name);
-			options.head_name = NULL;
+			FREE_AND_NULL(options.head_name);
 			branch_name = "HEAD";
 		}
 		if (get_oid("HEAD", &options.orig_head))
@@ -1769,7 +1768,7 @@
 	 * we just fast-forwarded.
 	 */
 	strbuf_reset(&msg);
-	if (!oidcmp(&merge_base, &options.orig_head)) {
+	if (oideq(&merge_base, &options.orig_head)) {
 		printf(_("Fast-forwarded %s to %s.\n"),
 			branch_name, options.onto_name);
 		strbuf_addf(&msg, "rebase finished: %s onto %s",
diff --git a/builtin/repack.c b/builtin/repack.c
index 67f8978..caca113 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -14,7 +14,7 @@
 
 static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
-static int write_bitmaps;
+static int write_bitmaps = -1;
 static int use_delta_islands;
 static char *packdir, *packtmp;
 
@@ -343,6 +343,9 @@
 	    (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
 		die(_("--keep-unreachable and -A are incompatible"));
 
+	if (write_bitmaps < 0)
+		write_bitmaps = (pack_everything & ALL_INTO_ONE) &&
+				 is_bare_repository();
 	if (pack_kept_objects < 0)
 		pack_kept_objects = write_bitmaps;
 
diff --git a/builtin/replace.c b/builtin/replace.c
index f570162..644b21c 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -370,16 +370,19 @@
 	/* prepare new parents */
 	for (i = 0; i < argc; i++) {
 		struct object_id oid;
+		struct commit *commit;
+
 		if (get_oid(argv[i], &oid) < 0) {
 			strbuf_release(&new_parents);
 			return error(_("not a valid object name: '%s'"),
 				     argv[i]);
 		}
-		if (!lookup_commit_reference(the_repository, &oid)) {
+		commit = lookup_commit_reference(the_repository, &oid);
+		if (!commit) {
 			strbuf_release(&new_parents);
-			return error(_("could not parse %s"), argv[i]);
+			return error(_("could not parse %s as a commit"), argv[i]);
 		}
-		strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
+		strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&commit->object.oid));
 	}
 
 	/* replace existing parents with new ones */
@@ -478,15 +481,18 @@
 
 	strbuf_release(&buf);
 
-	if (oideq(&old_oid, &new_oid)) {
+	if (oideq(&commit->object.oid, &new_oid)) {
 		if (gentle) {
-			warning(_("graft for '%s' unnecessary"), oid_to_hex(&old_oid));
+			warning(_("graft for '%s' unnecessary"),
+				oid_to_hex(&commit->object.oid));
 			return 0;
 		}
-		return error(_("new commit is the same as the old one: '%s'"), oid_to_hex(&old_oid));
+		return error(_("new commit is the same as the old one: '%s'"),
+			     oid_to_hex(&commit->object.oid));
 	}
 
-	return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force);
+	return replace_object_oid(old_ref, &commit->object.oid,
+				  "replacement", &new_oid, force);
 }
 
 static int convert_graft_file(int force)
diff --git a/builtin/reset.c b/builtin/reset.c
index 7882829..26ef9a7 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -386,6 +386,7 @@
 			int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
 			if (read_from_tree(&pathspec, &oid, intent_to_add))
 				return 1;
+			the_index.updated_skipworktree = 1;
 			if (!quiet && get_git_work_tree()) {
 				uint64_t t_begin, t_delta_in_ms;
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 425a577..9f31837 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -379,7 +379,6 @@
 	repo_init_revisions(the_repository, &revs, prefix);
 	revs.abbrev = DEFAULT_ABBREV;
 	revs.commit_format = CMIT_FMT_UNSPECIFIED;
-	revs.do_not_die_on_missing_tree = 1;
 
 	/*
 	 * Scan the argument list before invoking setup_revisions(), so that we
@@ -409,6 +408,9 @@
 		}
 	}
 
+	if (arg_missing_action)
+		revs.do_not_die_on_missing_tree = 1;
+
 	argc = setup_revisions(argc, argv, &revs, &s_r_opt);
 
 	memset(&info, 0, sizeof(info));
diff --git a/builtin/revert.c b/builtin/revert.c
index a47b53c..d4dcedb 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -96,11 +96,13 @@
 {
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
+	const char *cleanup_arg = NULL;
 	int cmd = 0;
 	struct option base_options[] = {
 		OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'),
 		OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'),
 		OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'),
+		OPT_CLEANUP(&cleanup_arg),
 		OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
 		OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
 		OPT_NOOP_NOARG('r', NULL),
@@ -137,6 +139,11 @@
 	if (opts->keep_redundant_commits)
 		opts->allow_empty = 1;
 
+	if (cleanup_arg) {
+		opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1);
+		opts->explicit_cleanup = 1;
+	}
+
 	/* Check for incompatible command line arguments */
 	if (cmd) {
 		char *this_operation;
diff --git a/builtin/rm.c b/builtin/rm.c
index db85b33..90cbe89 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -110,7 +110,7 @@
 		const struct cache_entry *ce;
 		const char *name = list.entry[i].name;
 		struct object_id oid;
-		unsigned mode;
+		unsigned short mode;
 		int local_changes = 0;
 		int staged_changes = 0;
 
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 934e514..082daea 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -753,7 +753,8 @@
 				/* Ah, that is a date spec... */
 				timestamp_t at;
 				at = approxidate(reflog_base);
-				read_ref_at(ref, flags, at, -1, &oid, NULL,
+				read_ref_at(get_main_ref_store(the_repository),
+					    ref, flags, at, -1, &oid, NULL,
 					    NULL, NULL, &base);
 			}
 		}
@@ -765,7 +766,8 @@
 			timestamp_t timestamp;
 			int tz;
 
-			if (read_ref_at(ref, flags, 0, base + i, &oid, &logmsg,
+			if (read_ref_at(get_main_ref_store(the_repository),
+					ref, flags, 0, base + i, &oid, &logmsg,
 					&timestamp, &tz, NULL)) {
 				reflog = i;
 				break;
diff --git a/builtin/stash.c b/builtin/stash.c
index 8b82053..2a8e6d0 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1280,7 +1280,7 @@
 			ce_path_match(&the_index, active_cache[i], ps,
 				      ps_matched);
 
-		if (report_path_error(ps_matched, ps, NULL)) {
+		if (report_path_error(ps_matched, ps)) {
 			fprintf_ln(stderr, _("Did you forget to 'git add'?"));
 			ret = -1;
 			free(ps_matched);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6bcc4f1..8c72ea8 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -348,7 +348,7 @@
 			i++;
 	}
 
-	if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
+	if (ps_matched && report_path_error(ps_matched, pathspec))
 		result = -1;
 
 	free(ps_matched);
@@ -566,12 +566,12 @@
 	};
 
 	const char *const git_submodule_helper_usage[] = {
-		N_("git submodule--helper foreach [--quiet] [--recursive] <command>"),
+		N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"),
 		NULL
 	};
 
 	argc = parse_options(argc, argv, prefix, module_foreach_options,
-			     git_submodule_helper_usage, PARSE_OPT_KEEP_UNKNOWN);
+			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0)
 		return 1;
@@ -709,7 +709,7 @@
 	};
 
 	const char *const git_submodule_helper_usage[] = {
-		N_("git submodule--helper init [<path>]"),
+		N_("git submodule--helper init [<options>] [<path>]"),
 		NULL
 	};
 
@@ -2096,7 +2096,7 @@
 	};
 
 	const char *const git_submodule_helper_usage[] = {
-		N_("git submodule--helper embed-git-dir [<path>...]"),
+		N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"),
 		NULL
 	};
 
@@ -2147,17 +2147,22 @@
 static int module_config(int argc, const char **argv, const char *prefix)
 {
 	enum {
-		CHECK_WRITEABLE = 1
+		CHECK_WRITEABLE = 1,
+		DO_UNSET = 2
 	} command = 0;
 
 	struct option module_config_options[] = {
 		OPT_CMDMODE(0, "check-writeable", &command,
 			    N_("check if it is safe to write to the .gitmodules file"),
 			    CHECK_WRITEABLE),
+		OPT_CMDMODE(0, "unset", &command,
+			    N_("unset the config in the .gitmodules file"),
+			    DO_UNSET),
 		OPT_END()
 	};
 	const char *const git_submodule_helper_usage[] = {
-		N_("git submodule--helper config name [value]"),
+		N_("git submodule--helper config <name> [<value>]"),
+		N_("git submodule--helper config --unset <name>"),
 		N_("git submodule--helper config --check-writeable"),
 		NULL
 	};
@@ -2169,15 +2174,17 @@
 		return is_writing_gitmodules_ok() ? 0 : -1;
 
 	/* Equivalent to ACTION_GET in builtin/config.c */
-	if (argc == 2)
+	if (argc == 2 && command != DO_UNSET)
 		return print_config_from_gitmodules(the_repository, argv[1]);
 
 	/* Equivalent to ACTION_SET in builtin/config.c */
-	if (argc == 3) {
+	if (argc == 3 || (argc == 2 && command == DO_UNSET)) {
+		const char *value = (argc == 3) ? argv[2] : NULL;
+
 		if (!is_writing_gitmodules_ok())
 			die(_("please make sure that the .gitmodules file is in the working tree"));
 
-		return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
+		return config_set_in_gitmodules_file_gently(argv[1], value);
 	}
 
 	usage_with_options(git_submodule_helper_usage, module_config_options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 02f6bd1..1debd3a 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -22,10 +22,11 @@
 #include "ref-filter.h"
 
 static const char * const git_tag_usage[] = {
-	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
+	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
+		"\t\t<tagname> [<head>]"),
 	N_("git tag -d <tagname>..."),
-	N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
-		"\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
+	N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
+		"\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
 	N_("git tag -v [--format=<format>] <tagname>..."),
 	NULL
 };
@@ -205,7 +206,14 @@
 	} cleanup_mode;
 };
 
-static void create_tag(const struct object_id *object, const char *tag,
+static const char message_advice_nested_tag[] =
+	N_("You have created a nested tag. The object referred to by your new is\n"
+	   "already a tag. If you meant to tag the object that it points to, use:\n"
+	   "\n"
+	   "\tgit tag -f %s %s^{}");
+
+static void create_tag(const struct object_id *object, const char *object_ref,
+		       const char *tag,
 		       struct strbuf *buf, struct create_tag_options *opt,
 		       struct object_id *prev, struct object_id *result)
 {
@@ -215,7 +223,10 @@
 
 	type = oid_object_info(the_repository, object, NULL);
 	if (type <= OBJ_NONE)
-	    die(_("bad object type."));
+		die(_("bad object type."));
+
+	if (type == OBJ_TAG && advice_nested_tag)
+		advise(_(message_advice_nested_tag), tag, object_ref);
 
 	strbuf_addf(&header,
 		    "object %s\n"
@@ -397,8 +408,7 @@
 		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
 		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
-		OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
-			N_("how to strip spaces and #comments from message")),
+		OPT_CLEANUP(&cleanup_arg),
 		OPT_STRING('u', "local-user", &keyid, N_("key-id"),
 					N_("use another key to sign the tag")),
 		OPT__FORCE(&force, N_("replace the tag if exists"), 0),
@@ -412,8 +422,7 @@
 		OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
 		OPT_MERGED(&filter, N_("print only tags that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
-		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
-			     N_("field name to sort on"), &parse_opt_ref_sorting),
+		OPT_REF_SORT(sorting_tail),
 		{
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 			N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
@@ -550,7 +559,7 @@
 	if (create_tag_object) {
 		if (force_sign_annotate && !annotate)
 			opt.sign = 1;
-		create_tag(&object, tag, &buf, &opt, &prev, &object);
+		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
 	}
 
 	transaction = ref_transaction_begin(&err);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 1b6c42f..27db092 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -597,7 +597,7 @@
 					struct object_id *ent, const char *path,
 					int namelen, int stage)
 {
-	unsigned mode;
+	unsigned short mode;
 	struct object_id oid;
 	struct cache_entry *ce;
 
@@ -724,7 +724,7 @@
 }
 
 static int do_reupdate(int ac, const char **av,
-		       const char *prefix, int prefix_length)
+		       const char *prefix)
 {
 	/* Read HEAD and run update-index on paths that are
 	 * merged and already different between index and HEAD.
@@ -940,8 +940,7 @@
 
 	/* consume remaining arguments. */
 	setup_work_tree();
-	*has_errors = do_reupdate(ctx->argc, ctx->argv,
-				prefix, prefix ? strlen(prefix) : 0);
+	*has_errors = do_reupdate(ctx->argc, ctx->argv, prefix);
 	if (*has_errors)
 		active_cache_changed = 0;
 
@@ -1082,6 +1081,8 @@
 	if (entries < 0)
 		die("cache corrupted");
 
+	the_index.updated_skipworktree = 1;
+
 	/*
 	 * Custom copy of parse_options() because we want to handle
 	 * filename arguments as they come.
diff --git a/bulk-checkin.h b/bulk-checkin.h
index f438f93..b26f3dc 100644
--- a/bulk-checkin.h
+++ b/bulk-checkin.h
@@ -6,11 +6,11 @@
 
 #include "cache.h"
 
-extern int index_bulk_checkin(struct object_id *oid,
-			      int fd, size_t size, enum object_type type,
-			      const char *path, unsigned flags);
+int index_bulk_checkin(struct object_id *oid,
+		       int fd, size_t size, enum object_type type,
+		       const char *path, unsigned flags);
 
-extern void plug_bulk_checkin(void);
-extern void unplug_bulk_checkin(void);
+void plug_bulk_checkin(void);
+void unplug_bulk_checkin(void);
 
 #endif
diff --git a/cache.h b/cache.h
index e3c2ab9..fa8ede9 100644
--- a/cache.h
+++ b/cache.h
@@ -339,7 +339,9 @@
 	struct cache_time timestamp;
 	unsigned name_hash_initialized : 1,
 		 initialized : 1,
-		 drop_cache_tree : 1;
+		 drop_cache_tree : 1,
+		 updated_workdir : 1,
+		 updated_skipworktree : 1;
 	struct hashmap name_hash;
 	struct hashmap dir_hash;
 	struct object_id oid;
@@ -350,10 +352,10 @@
 };
 
 /* Name hashing */
-extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
-extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
-extern void free_name_hash(struct index_state *istate);
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
+void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+void free_name_hash(struct index_state *istate);
 
 
 /* Cache entry creation and cleanup */
@@ -543,7 +545,7 @@
  */
 extern const char * const local_repo_env[];
 
-extern void setup_git_env(const char *git_dir);
+void setup_git_env(const char *git_dir);
 
 /*
  * Returns true iff we have a configured git repository (either via
@@ -552,29 +554,29 @@
 int have_git_dir(void);
 
 extern int is_bare_repository_cfg;
-extern int is_bare_repository(void);
-extern int is_inside_git_dir(void);
+int is_bare_repository(void);
+int is_inside_git_dir(void);
 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 char *get_object_directory(void);
-extern char *get_index_file(void);
-extern char *get_graft_file(struct repository *r);
-extern void set_git_dir(const char *path);
-extern int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
-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_super_prefix(void);
-extern const char *get_git_work_tree(void);
+int is_inside_work_tree(void);
+const char *get_git_dir(void);
+const char *get_git_common_dir(void);
+char *get_object_directory(void);
+char *get_index_file(void);
+char *get_graft_file(struct repository *r);
+void set_git_dir(const char *path);
+int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
+int get_common_dir(struct strbuf *sb, const char *gitdir);
+const char *get_git_namespace(void);
+const char *strip_namespace(const char *namespaced_ref);
+const char *get_super_prefix(void);
+const char *get_git_work_tree(void);
 
 /*
  * Return true if the given path is a git directory; note that this _just_
  * looks at the directory itself. If you want to know whether "foo/.git"
  * is a repository, you must feed that path, not just "foo".
  */
-extern int is_git_directory(const char *path);
+int is_git_directory(const char *path);
 
 /*
  * Return 1 if the given path is the root of a git repository or
@@ -586,7 +588,7 @@
  * as we usually consider sub-repos precious, and would prefer to err on the
  * side of not disrupting or deleting them.
  */
-extern int is_nonbare_repository_dir(struct strbuf *path);
+int is_nonbare_repository_dir(struct strbuf *path);
 
 #define READ_GITFILE_ERR_STAT_FAILED 1
 #define READ_GITFILE_ERR_NOT_A_FILE 2
@@ -596,17 +598,17 @@
 #define READ_GITFILE_ERR_NO_PATH 6
 #define READ_GITFILE_ERR_NOT_A_REPO 7
 #define READ_GITFILE_ERR_TOO_LARGE 8
-extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
-extern const char *read_gitfile_gently(const char *path, int *return_error_code);
+void read_gitfile_error_die(int error_code, const char *path, const char *dir);
+const char *read_gitfile_gently(const char *path, int *return_error_code);
 #define read_gitfile(path) read_gitfile_gently((path), NULL)
-extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
+const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 
-extern void set_git_work_tree(const char *tree);
+void set_git_work_tree(const char *tree);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
-extern void setup_work_tree(void);
+void setup_work_tree(void);
 /*
  * Find the commondir and gitdir of the repository that contains the current
  * working directory, without changing the working directory or other global
@@ -615,12 +617,12 @@
  * both have the same result appended to the buffer.  The return value is
  * either 0 upon success and non-zero if no repository was found.
  */
-extern int discover_git_directory(struct strbuf *commondir,
-				  struct strbuf *gitdir);
-extern const char *setup_git_directory_gently(int *);
-extern const char *setup_git_directory(void);
-extern char *prefix_path(const char *prefix, int len, const char *path);
-extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
+int discover_git_directory(struct strbuf *commondir,
+			   struct strbuf *gitdir);
+const char *setup_git_directory_gently(int *);
+const char *setup_git_directory(void);
+char *prefix_path(const char *prefix, int len, const char *path);
+char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
 
 /*
  * Concatenate "prefix" (if len is non-zero) and "path", with no
@@ -632,23 +634,23 @@
  * The return value is always a newly allocated string (even if the
  * prefix was empty).
  */
-extern char *prefix_filename(const char *prefix, const char *path);
+char *prefix_filename(const char *prefix, const char *path);
 
-extern int check_filename(const char *prefix, const char *name);
-extern void verify_filename(const char *prefix,
-			    const char *name,
-			    int diagnose_misspelt_rev);
-extern void verify_non_filename(const char *prefix, const char *name);
-extern int path_inside_repo(const char *prefix, const char *path);
+int check_filename(const char *prefix, const char *name);
+void verify_filename(const char *prefix,
+		     const char *name,
+		     int diagnose_misspelt_rev);
+void verify_non_filename(const char *prefix, const char *name);
+int path_inside_repo(const char *prefix, const char *path);
 
 #define INIT_DB_QUIET 0x0001
 #define INIT_DB_EXIST_OK 0x0002
 
-extern int init_db(const char *git_dir, const char *real_git_dir,
-		   const char *template_dir, unsigned int flags);
+int init_db(const char *git_dir, const char *real_git_dir,
+	    const char *template_dir, unsigned int flags);
 
-extern void sanitize_stdfds(void);
-extern int daemonize(void);
+void sanitize_stdfds(void);
+int daemonize(void);
 
 #define alloc_nr(x) (((x)+16)*3/2)
 
@@ -672,14 +674,14 @@
 
 /* Initialize and use the cache information */
 struct lock_file;
-extern void preload_index(struct index_state *index,
-			  const struct pathspec *pathspec,
-			  unsigned int refresh_flags);
-extern int do_read_index(struct index_state *istate, const char *path,
-			 int must_exist); /* for testting only! */
-extern int read_index_from(struct index_state *, const char *path,
-			   const char *gitdir);
-extern int is_index_unborn(struct index_state *);
+void preload_index(struct index_state *index,
+		   const struct pathspec *pathspec,
+		   unsigned int refresh_flags);
+int do_read_index(struct index_state *istate, const char *path,
+		  int must_exist); /* for testting only! */
+int read_index_from(struct index_state *, const char *path,
+		    const char *gitdir);
+int is_index_unborn(struct index_state *);
 
 /* For use with `write_locked_index()`. */
 #define COMMIT_LOCK		(1 << 0)
@@ -704,11 +706,11 @@
  * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
  * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
  */
-extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 
-extern int discard_index(struct index_state *);
-extern void move_index_extensions(struct index_state *dst, struct index_state *src);
-extern int unmerged_index(const struct index_state *);
+int discard_index(struct index_state *);
+void move_index_extensions(struct index_state *dst, struct index_state *src);
+int unmerged_index(const struct index_state *);
 
 /**
  * Returns 1 if istate differs from tree, 0 otherwise.  If tree is NULL,
@@ -717,15 +719,15 @@
  * provided, the space-separated list of files that differ will be appended
  * to it.
  */
-extern int repo_index_has_changes(struct repository *repo,
-				  struct tree *tree,
-				  struct strbuf *sb);
+int repo_index_has_changes(struct repository *repo,
+			   struct tree *tree,
+			   struct strbuf *sb);
 
-extern int verify_path(const char *path, unsigned mode);
-extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
-extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
-extern void adjust_dirname_case(struct index_state *istate, char *name);
-extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+int verify_path(const char *path, unsigned mode);
+int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
+int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+void adjust_dirname_case(struct index_state *istate, char *name);
+struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 
 /*
  * Searches for an entry defined by name and namelen in the given index.
@@ -744,7 +746,7 @@
  * index_name_pos(&index, "f", 1) -> -3
  * index_name_pos(&index, "g", 1) -> -5
  */
-extern int index_name_pos(const struct index_state *, const char *name, int namelen);
+int index_name_pos(const struct index_state *, const char *name, int namelen);
 
 #define ADD_CACHE_OK_TO_ADD 1		/* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
@@ -753,14 +755,14 @@
 #define ADD_CACHE_NEW_ONLY 16		/* Do not replace existing ones */
 #define ADD_CACHE_KEEP_CACHE_TREE 32	/* Do not invalidate cache-tree */
 #define ADD_CACHE_RENORMALIZE 64        /* Pass along HASH_RENORMALIZE */
-extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
+int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
+void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 
 /* Remove entry, return true if there are more entries to go. */
-extern int remove_index_entry_at(struct index_state *, int pos);
+int remove_index_entry_at(struct index_state *, int pos);
 
-extern void remove_marked_cache_entries(struct index_state *istate, int invalidate);
-extern int remove_file_from_index(struct index_state *, const char *path);
+void remove_marked_cache_entries(struct index_state *istate, int invalidate);
+int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS	4
@@ -775,14 +777,14 @@
  * the latter will do necessary lstat(2) internally before
  * calling the former.
  */
-extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
-extern int add_file_to_index(struct index_state *, const char *path, int flags);
+int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
+int add_file_to_index(struct index_state *, const char *path, int flags);
 
-extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
-extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
-extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
-extern int index_name_is_other(const struct index_state *, const char *, int);
-extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
+int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
+void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
+int index_name_is_other(const struct index_state *, const char *, int);
+void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID		01
@@ -796,22 +798,22 @@
 #define CE_MATCH_REFRESH		0x10
 /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 #define CE_MATCH_IGNORE_FSMONITOR 0X20
-extern int is_racy_timestamp(const struct index_state *istate,
-			     const struct cache_entry *ce);
-extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+int is_racy_timestamp(const struct index_state *istate,
+		      const struct cache_entry *ce);
+int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
 #define HASH_WRITE_OBJECT 1
 #define HASH_FORMAT_CHECK 2
 #define HASH_RENORMALIZE  4
-extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
 
 /*
  * Record to sd the data from st that we use to check whether a file
  * might have changed.
  */
-extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+void fill_stat_data(struct stat_data *sd, struct stat *st);
 
 /*
  * Return 0 if st is consistent with a file not having been changed
@@ -819,11 +821,11 @@
  * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
  * 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);
+int match_stat_data(const struct stat_data *sd, struct stat *st);
+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);
+void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY		0x0001	/* ignore_valid */
 #define REFRESH_UNMERGED	0x0002	/* allow unmerged */
@@ -832,10 +834,10 @@
 #define REFRESH_IGNORE_SUBMODULES	0x0010	/* ignore submodules */
 #define REFRESH_IN_PORCELAIN	0x0020	/* user friendly output, not "needs update" */
 #define REFRESH_PROGRESS	0x0040  /* show progress bar if stderr is tty */
-extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
-extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
+int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
+struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
 
-extern void set_alternate_index_output(const char *);
+void set_alternate_index_output(const char *);
 
 extern int verify_index_checksum;
 extern int verify_ce_order;
@@ -1020,7 +1022,7 @@
  * set_git_dir() before calling this, and use it only for "are we in a valid
  * repo?".
  */
-extern void check_repository_format(void);
+void check_repository_format(void);
 
 #define MTIME_CHANGED	0x0001
 #define CTIME_CHANGED	0x0002
@@ -1045,8 +1047,10 @@
  * Note that while this version avoids the static buffer, it is not fully
  * reentrant, as it calls into other non-reentrant git code.
  */
-extern const char *find_unique_abbrev(const struct object_id *oid, int len);
-extern int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len);
+const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
+#define find_unique_abbrev(oid, len) repo_find_unique_abbrev(the_repository, oid, len)
+int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
+#define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len)
 
 extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
 extern const struct object_id null_oid;
@@ -1242,7 +1246,7 @@
 int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 
 int mkdir_in_gitdir(const char *path);
-extern char *expand_user_path(const char *path, int real_home);
+char *expand_user_path(const char *path, int real_home);
 const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
@@ -1286,26 +1290,26 @@
  * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
  * "$HOME/.config/git/$filename". Return NULL upon error.
  */
-extern char *xdg_config_home(const char *filename);
+char *xdg_config_home(const char *filename);
 
 /**
  * Return a newly allocated string with the evaluation of
  * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
  * "$HOME/.cache/git/$filename". Return NULL upon error.
  */
-extern char *xdg_cache_home(const char *filename);
+char *xdg_cache_home(const char *filename);
 
-extern int git_open_cloexec(const char *name, int flags);
+int git_open_cloexec(const char *name, int flags);
 #define git_open(name) git_open_cloexec(name, O_RDONLY)
-extern int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
-extern int parse_loose_header(const char *hdr, unsigned long *sizep);
+int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
+int parse_loose_header(const char *hdr, unsigned long *sizep);
 
-extern int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
+int check_object_signature(const struct object_id *oid, void *buf, unsigned long size, const char *type);
 
-extern int finalize_object_file(const char *tmpfile, const char *filename);
+int finalize_object_file(const char *tmpfile, const char *filename);
 
 /* Helper to check and "touch" a file */
-extern int check_and_freshen_file(const char *fn, int freshen);
+int check_and_freshen_file(const char *fn, int freshen);
 
 extern const signed char hexval_table[256];
 static inline unsigned int hexval(unsigned char c)
@@ -1331,7 +1335,7 @@
 #define FALLBACK_DEFAULT_ABBREV 7
 
 struct object_context {
-	unsigned mode;
+	unsigned short mode;
 	/*
 	 * symlink_path is only used by get_tree_entry_follow_symlinks,
 	 * and only for symlinks that point outside the repository.
@@ -1378,22 +1382,34 @@
 		       */
 };
 
-extern int get_oid(const char *str, struct object_id *oid);
-extern int get_oidf(struct object_id *oid, const char *fmt, ...);
-extern int get_oid_commit(const char *str, struct object_id *oid);
-extern int get_oid_committish(const char *str, struct object_id *oid);
-extern int get_oid_tree(const char *str, struct object_id *oid);
-extern int get_oid_treeish(const char *str, struct object_id *oid);
-extern int get_oid_blob(const char *str, struct object_id *oid);
-extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
-				unsigned flags, struct object_id *oid,
-				struct object_context *oc);
+int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
+int get_oidf(struct object_id *oid, const char *fmt, ...);
+int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid);
+int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid);
+void maybe_die_on_misspelt_object_name(struct repository *repo,
+				       const char *name,
+				       const char *prefix);
+enum get_oid_result get_oid_with_context(struct repository *repo, const char *str,
+					 unsigned flags, struct object_id *oid,
+					 struct object_context *oc);
+
+#define get_oid(str, oid)		repo_get_oid(the_repository, str, oid)
+#define get_oid_commit(str, oid)	repo_get_oid_commit(the_repository, str, oid)
+#define get_oid_committish(str, oid)	repo_get_oid_committish(the_repository, str, oid)
+#define get_oid_tree(str, oid)		repo_get_oid_tree(the_repository, str, oid)
+#define get_oid_treeish(str, oid)	repo_get_oid_treeish(the_repository, str, oid)
+#define get_oid_blob(str, oid)		repo_get_oid_blob(the_repository, str, oid)
+#define get_oid_mb(str, oid) 		repo_get_oid_mb(the_repository, str, oid)
 
 typedef int each_abbrev_fn(const struct object_id *oid, void *);
-extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
+int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *);
+#define for_each_abbrev(prefix, fn, data) repo_for_each_abbrev(the_repository, prefix, fn, data)
 
-extern int set_disambiguate_hint_config(const char *var, const char *value);
+int set_disambiguate_hint_config(const char *var, const char *value);
 
 /*
  * Try to read a SHA1 in hexadecimal format from the 40 characters
@@ -1402,15 +1418,15 @@
  * input, so it is safe to pass this function an arbitrary
  * null-terminated string.
  */
-extern int get_sha1_hex(const char *hex, unsigned char *sha1);
-extern int get_oid_hex(const char *hex, struct object_id *sha1);
+int get_sha1_hex(const char *hex, unsigned char *sha1);
+int get_oid_hex(const char *hex, struct object_id *sha1);
 
 /*
  * Read `len` pairs of hexadecimal digits from `hex` and write the
  * values to `binary` as `len` bytes. Return 0 on success, or -1 if
  * the input does not consist of hex digits).
  */
-extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
  * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
@@ -1438,7 +1454,7 @@
  * other invalid character.  end is only updated on success; otherwise, it is
  * unmodified.
  */
-extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
 
 /*
  * This reads short-hand syntax that not only evaluates to a commit
@@ -1469,24 +1485,30 @@
 #define INTERPRET_BRANCH_LOCAL (1<<0)
 #define INTERPRET_BRANCH_REMOTE (1<<1)
 #define INTERPRET_BRANCH_HEAD (1<<2)
-extern int interpret_branch_name(const char *str, int len, struct strbuf *,
-				 unsigned allowed);
-extern int get_oid_mb(const char *str, struct object_id *oid);
+int repo_interpret_branch_name(struct repository *r,
+			       const char *str, int len,
+			       struct strbuf *buf,
+			       unsigned allowed);
+#define interpret_branch_name(str, len, buf, allowed) \
+	repo_interpret_branch_name(the_repository, str, len, buf, allowed)
 
-extern int validate_headref(const char *ref);
+int validate_headref(const char *ref);
 
-extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
-extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
-extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
+int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
+int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
 
-extern void *read_object_with_reference(const struct object_id *oid,
-					const char *required_type,
-					unsigned long *size,
-					struct object_id *oid_ret);
+void *read_object_with_reference(const struct object_id *oid,
+				 const char *required_type,
+				 unsigned long *size,
+				 struct object_id *oid_ret);
 
-extern struct object *peel_to_type(const char *name, int namelen,
-				   struct object *o, enum object_type);
+struct object *repo_peel_to_type(struct repository *r,
+				 const char *name, int namelen,
+				 struct object *o, enum object_type);
+#define peel_to_type(name, namelen, obj, type) \
+	repo_peel_to_type(the_repository, name, namelen, obj, type)
 
 enum date_mode_type {
 	DATE_NORMAL = 0,
@@ -1540,24 +1562,24 @@
 	WANT_COMMITTER_IDENT
 };
 
-extern const char *git_author_info(int);
-extern const char *git_committer_info(int);
-extern const char *fmt_ident(const char *name, const char *email,
-		enum want_ident whose_ident,
-		const char *date_str, int);
-extern const char *fmt_name(enum want_ident);
-extern const char *ident_default_name(void);
-extern const char *ident_default_email(void);
-extern const char *git_editor(void);
-extern const char *git_sequence_editor(void);
-extern const char *git_pager(int stdout_is_tty);
-extern int is_terminal_dumb(void);
-extern int git_ident_config(const char *, const char *, void *);
+const char *git_author_info(int);
+const char *git_committer_info(int);
+const char *fmt_ident(const char *name, const char *email,
+		      enum want_ident whose_ident,
+		      const char *date_str, int);
+const char *fmt_name(enum want_ident);
+const char *ident_default_name(void);
+const char *ident_default_email(void);
+const char *git_editor(void);
+const char *git_sequence_editor(void);
+const char *git_pager(int stdout_is_tty);
+int is_terminal_dumb(void);
+int git_ident_config(const char *, const char *, void *);
 /*
  * Prepare an ident to fall back on if the user didn't configure it.
  */
 void prepare_fallback_ident(const char *name, const char *email);
-extern void reset_ident_date(void);
+void reset_ident_date(void);
 
 struct ident_split {
 	const char *name_begin;
@@ -1573,7 +1595,7 @@
  * Signals an success with 0, but time part of the result may be NULL
  * if the input lacks timestamp and zone
  */
-extern int split_ident_line(struct ident_split *, const char *, int);
+int split_ident_line(struct ident_split *, const char *, int);
 
 /*
  * Like show_date, but pull the timestamp and tz parameters from
@@ -1590,7 +1612,7 @@
  * Because there are two fields, we must choose one as the primary key; we
  * currently arbitrarily pick the email.
  */
-extern int ident_cmp(const struct ident_split *, const struct ident_split *);
+int ident_cmp(const struct ident_split *, const struct ident_split *);
 
 struct checkout {
 	struct index_state *istate;
@@ -1606,14 +1628,14 @@
 #define CHECKOUT_INIT { NULL, "" }
 
 #define TEMPORARY_FILENAME_LENGTH 25
-extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
-extern void enable_delayed_checkout(struct checkout *state);
-extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
+int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
+void enable_delayed_checkout(struct checkout *state);
+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
-extern void unlink_entry(const struct cache_entry *ce);
+void unlink_entry(const struct cache_entry *ce);
 
 struct cache_def {
 	struct strbuf path;
@@ -1627,12 +1649,12 @@
 	strbuf_release(&cache->path);
 }
 
-extern int has_symlink_leading_path(const char *name, int len);
-extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-extern int check_leading_path(const char *name, int len);
-extern int has_dirs_only_path(const char *name, int len, int prefix_len);
-extern void schedule_dir_for_removal(const char *name, int len);
-extern void remove_scheduled_dirs(void);
+int has_symlink_leading_path(const char *name, int len);
+int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
+int check_leading_path(const char *name, int len);
+int has_dirs_only_path(const char *name, int len, int prefix_len);
+void schedule_dir_for_removal(const char *name, int len);
+void remove_scheduled_dirs(void);
 
 struct pack_window {
 	struct pack_window *next;
@@ -1654,14 +1676,14 @@
  * usual "XXXXXX" trailer, and the resulting filename is written into the
  * "template" buffer. Returns the open descriptor.
  */
-extern int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
 
 /*
  * Create a pack .keep file named "name" (which should generally be the output
  * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
  * error.
  */
-extern int odb_pack_keep(const char *name);
+int odb_pack_keep(const char *name);
 
 /*
  * Set this to 0 to prevent oid_object_info_extended() from fetching missing
@@ -1672,10 +1694,10 @@
 extern int fetch_if_missing;
 
 /* Dumb servers support */
-extern int update_server_info(int);
+int update_server_info(int);
 
-extern const char *get_log_output_encoding(void);
-extern const char *get_commit_output_encoding(void);
+const char *get_log_output_encoding(void);
+const char *get_commit_output_encoding(void);
 
 /*
  * This is a hack for test programs like test-dump-untracked-cache to
@@ -1684,8 +1706,8 @@
  */
 extern int ignore_untracked_cache_config;
 
-extern int committer_ident_sufficiently_given(void);
-extern int author_ident_sufficiently_given(void);
+int committer_ident_sufficiently_given(void);
+int author_ident_sufficiently_given(void);
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
@@ -1693,22 +1715,22 @@
 extern const char *git_mailmap_blob;
 
 /* IO helper functions */
-extern void maybe_flush_or_die(FILE *, const char *);
+void maybe_flush_or_die(FILE *, const char *);
 __attribute__((format (printf, 2, 3)))
-extern void fprintf_or_die(FILE *, const char *fmt, ...);
+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);
+int copy_fd(int ifd, int ofd);
+int copy_file(const char *dst, const char *src, int mode);
+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 void fsync_or_die(int fd, const char *);
+void write_or_die(int fd, const void *buf, size_t count);
+void fsync_or_die(int fd, const char *);
 
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
-extern ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
+ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t write_in_full(int fd, const void *buf, size_t count);
+ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
 
 static inline ssize_t write_str_in_full(int fd, const char *str)
 {
@@ -1719,7 +1741,7 @@
  * Open (and truncate) the file at path, write the contents of buf to it,
  * and close it. Dies if any errors are encountered.
  */
-extern void write_file_buf(const char *path, const char *buf, size_t len);
+void write_file_buf(const char *path, const char *buf, size_t len);
 
 /**
  * Like write_file_buf(), but format the contents into a buffer first.
@@ -1729,16 +1751,16 @@
  *   write_file(path, "counter: %d", ctr);
  */
 __attribute__((format (printf, 2, 3)))
-extern void write_file(const char *path, const char *fmt, ...);
+void write_file(const char *path, const char *fmt, ...);
 
 /* pager.c */
-extern void setup_pager(void);
-extern int pager_in_use(void);
+void setup_pager(void);
+int pager_in_use(void);
 extern int pager_use_color;
-extern int term_columns(void);
-extern int decimal_width(uintmax_t);
-extern int check_pager_config(const char *cmd);
-extern void prepare_pager_args(struct child_process *, const char *pager);
+int term_columns(void);
+int decimal_width(uintmax_t);
+int check_pager_config(const char *cmd);
+void prepare_pager_args(struct child_process *, const char *pager);
 
 extern const char *editor_program;
 extern const char *askpass_program;
@@ -1782,13 +1804,13 @@
 /* All WS_* -- when extended, adapt diff.c emit_symbol */
 #define WS_RULE_MASK           07777
 extern unsigned whitespace_rule_cfg;
-extern unsigned whitespace_rule(struct index_state *, const char *);
-extern unsigned parse_whitespace_rule(const char *);
-extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
-extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
-extern char *whitespace_error_string(unsigned ws);
-extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
+unsigned whitespace_rule(struct index_state *, const char *);
+unsigned parse_whitespace_rule(const char *);
+unsigned ws_check(const char *line, int len, unsigned ws_rule);
+void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
+char *whitespace_error_string(unsigned ws);
+void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
+int ws_blank_line(const char *line, int len, unsigned ws_rule);
 #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
 
 /* ls-files */
@@ -1858,9 +1880,9 @@
  * Should we print an ellipsis after an abbreviated SHA-1 value
  * when doing diff-raw output or indicating a detached HEAD?
  */
-extern int print_sha1_ellipsis(void);
+int print_sha1_ellipsis(void);
 
 /* Return 1 if the file is empty or does not exists, 0 otherwise. */
-extern int is_empty_or_missing_file(const char *filename);
+int is_empty_or_missing_file(const char *filename);
 
 #endif /* CACHE_H */
diff --git a/checkout.h b/checkout.h
index 6b20733..1152133 100644
--- a/checkout.h
+++ b/checkout.h
@@ -8,8 +8,8 @@
  * tracking branch.  Return the name of the remote if such a branch
  * exists, NULL otherwise.
  */
-extern const char *unique_tracking_name(const char *name,
-					struct object_id *oid,
-					int *dwim_remotes_matched);
+const char *unique_tracking_name(const char *name,
+				 struct object_id *oid,
+				 int *dwim_remotes_matched);
 
 #endif /* CHECKOUT_H */
diff --git a/column.h b/column.h
index 2567a5c..448c744 100644
--- a/column.h
+++ b/column.h
@@ -27,20 +27,20 @@
 };
 
 struct option;
-extern int parseopt_column_callback(const struct option *, const char *, int);
-extern int git_column_config(const char *var, const char *value,
-			     const char *command, unsigned int *colopts);
-extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
+int parseopt_column_callback(const struct option *, const char *, int);
+int git_column_config(const char *var, const char *value,
+		      const char *command, unsigned int *colopts);
+int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
 static inline int column_active(unsigned int colopts)
 {
 	return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
 }
 
 struct string_list;
-extern void print_columns(const struct string_list *list, unsigned int colopts,
-			  const struct column_options *opts);
+void print_columns(const struct string_list *list, unsigned int colopts,
+		   const struct column_options *opts);
 
-extern int run_column_filter(int colopts, const struct column_options *);
-extern int stop_column_filter(void);
+int run_column_filter(int colopts, const struct column_options *);
+int stop_column_filter(void);
 
 #endif
diff --git a/commit-graph.c b/commit-graph.c
index 47e9be0..7bcc9bb 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -80,25 +80,30 @@
 	return 1;
 }
 
-struct commit_graph *load_commit_graph_one(const char *graph_file)
+int open_commit_graph(const char *graph_file, int *fd, struct stat *st)
+{
+	*fd = git_open(graph_file);
+	if (*fd < 0)
+		return 0;
+	if (fstat(*fd, st)) {
+		close(*fd);
+		return 0;
+	}
+	return 1;
+}
+
+struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st)
 {
 	void *graph_map;
 	size_t graph_size;
-	struct stat st;
 	struct commit_graph *ret;
-	int fd = git_open(graph_file);
 
-	if (fd < 0)
-		return NULL;
-	if (fstat(fd, &st)) {
-		close(fd);
-		return NULL;
-	}
-	graph_size = xsize_t(st.st_size);
+	graph_size = xsize_t(st->st_size);
 
 	if (graph_size < GRAPH_MIN_SIZE) {
 		close(fd);
-		die(_("graph file %s is too small"), graph_file);
+		error(_("commit-graph file is too small"));
+		return NULL;
 	}
 	graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
 	ret = parse_commit_graph(graph_map, fd, graph_size);
@@ -106,12 +111,41 @@
 	if (!ret) {
 		munmap(graph_map, graph_size);
 		close(fd);
-		exit(1);
 	}
 
 	return ret;
 }
 
+static int verify_commit_graph_lite(struct commit_graph *g)
+{
+	/*
+	 * Basic validation shared between parse_commit_graph()
+	 * which'll be called every time the graph is used, and the
+	 * much more expensive verify_commit_graph() used by
+	 * "commit-graph verify".
+	 *
+	 * There should only be very basic checks here to ensure that
+	 * we don't e.g. segfault in fill_commit_in_graph(), but
+	 * because this is a very hot codepath nothing that e.g. loops
+	 * over g->num_commits, or runs a checksum on the commit-graph
+	 * itself.
+	 */
+	if (!g->chunk_oid_fanout) {
+		error("commit-graph is missing the OID Fanout chunk");
+		return 1;
+	}
+	if (!g->chunk_oid_lookup) {
+		error("commit-graph is missing the OID Lookup chunk");
+		return 1;
+	}
+	if (!g->chunk_commit_data) {
+		error("commit-graph is missing the Commit Data chunk");
+		return 1;
+	}
+
+	return 0;
+}
+
 struct commit_graph *parse_commit_graph(void *graph_map, int fd,
 					size_t graph_size)
 {
@@ -133,21 +167,21 @@
 
 	graph_signature = get_be32(data);
 	if (graph_signature != GRAPH_SIGNATURE) {
-		error(_("graph signature %X does not match signature %X"),
+		error(_("commit-graph signature %X does not match signature %X"),
 		      graph_signature, GRAPH_SIGNATURE);
 		return NULL;
 	}
 
 	graph_version = *(unsigned char*)(data + 4);
 	if (graph_version != GRAPH_VERSION) {
-		error(_("graph version %X does not match version %X"),
+		error(_("commit-graph version %X does not match version %X"),
 		      graph_version, GRAPH_VERSION);
 		return NULL;
 	}
 
 	hash_version = *(unsigned char*)(data + 5);
 	if (hash_version != oid_version()) {
-		error(_("hash version %X does not match version %X"),
+		error(_("commit-graph hash version %X does not match version %X"),
 		      hash_version, oid_version());
 		return NULL;
 	}
@@ -170,7 +204,7 @@
 
 		if (data + graph_size - chunk_lookup <
 		    GRAPH_CHUNKLOOKUP_WIDTH) {
-			error(_("chunk lookup table entry missing; graph file may be incomplete"));
+			error(_("commit-graph chunk lookup table entry missing; file may be incomplete"));
 			free(graph);
 			return NULL;
 		}
@@ -181,7 +215,7 @@
 		chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
 
 		if (chunk_offset > graph_size - the_hash_algo->rawsz) {
-			error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
+			error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
 			      (uint32_t)chunk_offset);
 			free(graph);
 			return NULL;
@@ -218,7 +252,7 @@
 		}
 
 		if (chunk_repeated) {
-			error(_("chunk id %08x appears multiple times"), chunk_id);
+			error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
 			free(graph);
 			return NULL;
 		}
@@ -233,9 +267,25 @@
 		last_chunk_offset = chunk_offset;
 	}
 
+	if (verify_commit_graph_lite(graph))
+		return NULL;
+
 	return graph;
 }
 
+static struct commit_graph *load_commit_graph_one(const char *graph_file)
+{
+
+	struct stat st;
+	int fd;
+	int open_ok = open_commit_graph(graph_file, &fd, &st);
+
+	if (!open_ok)
+		return NULL;
+
+	return load_commit_graph_one_fd_st(fd, &st);
+}
+
 static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
 {
 	char *graph_name;
@@ -261,6 +311,10 @@
 	struct object_directory *odb;
 	int config_value;
 
+	if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
+		die("dying as requested by the '%s' variable on commit-graph load!",
+		    GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
+
 	if (r->objects->commit_graph_attempted)
 		return !!r->objects->commit_graph;
 	r->objects->commit_graph_attempted = 1;
@@ -343,6 +397,11 @@
 	item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+	c->maybe_tree = t;
+}
+
 static int fill_commit_in_graph(struct repository *r,
 				struct commit *item,
 				struct commit_graph *g, uint32_t pos)
@@ -356,7 +415,7 @@
 	item->object.parsed = 1;
 	item->graph_pos = pos;
 
-	item->maybe_tree = NULL;
+	set_commit_tree(item, NULL);
 
 	date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
 	date_low = get_be32(commit_data + g->hash_len + 12);
@@ -442,7 +501,7 @@
 					   GRAPH_DATA_WIDTH * (c->graph_pos);
 
 	hashcpy(oid.hash, commit_data);
-	c->maybe_tree = lookup_tree(r, &oid);
+	set_commit_tree(c, lookup_tree(r, &oid));
 
 	return c->maybe_tree;
 }
@@ -525,7 +584,7 @@
 		uint32_t packedDate[2];
 		display_progress(progress, ++*progress_cnt);
 
-		parse_commit(*list);
+		parse_commit_no_graph(*list);
 		hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
 
 		parent = (*list)->parents;
@@ -722,7 +781,7 @@
 		display_progress(progress, i + 1);
 		commit = lookup_commit(the_repository, &oids->list[i]);
 
-		if (commit && !parse_commit(commit))
+		if (commit && !parse_commit_no_graph(commit))
 			add_missing_parents(oids, commit);
 	}
 	stop_progress(&progress);
@@ -971,7 +1030,7 @@
 			continue;
 
 		commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]);
-		parse_commit(commits.list[commits.nr]);
+		parse_commit_no_graph(commits.list[commits.nr]);
 
 		for (parent = commits.list[commits.nr]->parents;
 		     parent; parent = parent->next)
@@ -1089,15 +1148,7 @@
 		return 1;
 	}
 
-	verify_commit_graph_error = 0;
-
-	if (!g->chunk_oid_fanout)
-		graph_report("commit-graph is missing the OID Fanout chunk");
-	if (!g->chunk_oid_lookup)
-		graph_report("commit-graph is missing the OID Lookup chunk");
-	if (!g->chunk_commit_data)
-		graph_report("commit-graph is missing the Commit Data chunk");
-
+	verify_commit_graph_error = verify_commit_graph_lite(g);
 	if (verify_commit_graph_error)
 		return verify_commit_graph_error;
 
@@ -1116,7 +1167,7 @@
 		hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
 
 		if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
-			graph_report("commit-graph has incorrect OID order: %s then %s",
+			graph_report(_("commit-graph has incorrect OID order: %s then %s"),
 				     oid_to_hex(&prev_oid),
 				     oid_to_hex(&cur_oid));
 
@@ -1126,14 +1177,14 @@
 			uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
 
 			if (i != fanout_value)
-				graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+				graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"),
 					     cur_fanout_pos, fanout_value, i);
 			cur_fanout_pos++;
 		}
 
 		graph_commit = lookup_commit(r, &cur_oid);
 		if (!parse_commit_in_graph_one(r, g, graph_commit))
-			graph_report("failed to parse %s from commit-graph",
+			graph_report(_("failed to parse commit %s from commit-graph"),
 				     oid_to_hex(&cur_oid));
 	}
 
@@ -1141,7 +1192,7 @@
 		uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
 
 		if (g->num_commits != fanout_value)
-			graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+			graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"),
 				     cur_fanout_pos, fanout_value, i);
 
 		cur_fanout_pos++;
@@ -1163,14 +1214,14 @@
 		graph_commit = lookup_commit(r, &cur_oid);
 		odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r));
 		if (parse_commit_internal(odb_commit, 0, 0)) {
-			graph_report("failed to parse %s from object database",
+			graph_report(_("failed to parse commit %s from object database for commit-graph"),
 				     oid_to_hex(&cur_oid));
 			continue;
 		}
 
 		if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid,
 			   get_commit_tree_oid(odb_commit)))
-			graph_report("root tree OID for commit %s in commit-graph is %s != %s",
+			graph_report(_("root tree OID for commit %s in commit-graph is %s != %s"),
 				     oid_to_hex(&cur_oid),
 				     oid_to_hex(get_commit_tree_oid(graph_commit)),
 				     oid_to_hex(get_commit_tree_oid(odb_commit)));
@@ -1180,13 +1231,13 @@
 
 		while (graph_parents) {
 			if (odb_parents == NULL) {
-				graph_report("commit-graph parent list for commit %s is too long",
+				graph_report(_("commit-graph parent list for commit %s is too long"),
 					     oid_to_hex(&cur_oid));
 				break;
 			}
 
 			if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid))
-				graph_report("commit-graph parent for %s is %s != %s",
+				graph_report(_("commit-graph parent for %s is %s != %s"),
 					     oid_to_hex(&cur_oid),
 					     oid_to_hex(&graph_parents->item->object.oid),
 					     oid_to_hex(&odb_parents->item->object.oid));
@@ -1199,16 +1250,16 @@
 		}
 
 		if (odb_parents != NULL)
-			graph_report("commit-graph parent list for commit %s terminates early",
+			graph_report(_("commit-graph parent list for commit %s terminates early"),
 				     oid_to_hex(&cur_oid));
 
 		if (!graph_commit->generation) {
 			if (generation_zero == GENERATION_NUMBER_EXISTS)
-				graph_report("commit-graph has generation number zero for commit %s, but non-zero elsewhere",
+				graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"),
 					     oid_to_hex(&cur_oid));
 			generation_zero = GENERATION_ZERO_EXISTS;
 		} else if (generation_zero == GENERATION_ZERO_EXISTS)
-			graph_report("commit-graph has non-zero generation number for commit %s, but zero elsewhere",
+			graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"),
 				     oid_to_hex(&cur_oid));
 
 		if (generation_zero == GENERATION_ZERO_EXISTS)
@@ -1223,13 +1274,13 @@
 			max_generation--;
 
 		if (graph_commit->generation != max_generation + 1)
-			graph_report("commit-graph generation for commit %s is %u != %u",
+			graph_report(_("commit-graph generation for commit %s is %u != %u"),
 				     oid_to_hex(&cur_oid),
 				     graph_commit->generation,
 				     max_generation + 1);
 
 		if (graph_commit->date != odb_commit->date)
-			graph_report("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime,
+			graph_report(_("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime),
 				     oid_to_hex(&cur_oid),
 				     graph_commit->date,
 				     odb_commit->date);
diff --git a/commit-graph.h b/commit-graph.h
index 096d8ba..7dfb8c8 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -7,10 +7,12 @@
 #include "cache.h"
 
 #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
+#define GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD "GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD"
 
 struct commit;
 
 char *get_commit_graph_filename(const char *obj_dir);
+int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
 
 /*
  * Given a commit struct, try to fill the commit struct info, including:
@@ -52,7 +54,7 @@
 	const unsigned char *chunk_extra_edges;
 };
 
-struct commit_graph *load_commit_graph_one(const char *graph_file);
+struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st);
 
 struct commit_graph *parse_commit_graph(void *graph_map, int fd,
 					size_t graph_size);
diff --git a/commit.c b/commit.c
index a5333c7..8fa1883 100644
--- a/commit.c
+++ b/commit.c
@@ -340,15 +340,21 @@
 	}
 }
 
-struct tree *get_commit_tree(const struct commit *commit)
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+	c->maybe_tree = t;
+}
+
+struct tree *repo_get_commit_tree(struct repository *r,
+				  const struct commit *commit)
 {
 	if (commit->maybe_tree || !commit->object.parsed)
 		return commit->maybe_tree;
 
-	if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
-		BUG("commit has NULL tree, but was not loaded from commit-graph");
+	if (commit->graph_pos != COMMIT_NOT_FROM_GRAPH)
+		return get_commit_tree_in_graph(r, commit);
 
-	return get_commit_tree_in_graph(the_repository, commit);
+	return NULL;
 }
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
@@ -358,7 +364,7 @@
 
 void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
 {
-	c->maybe_tree = NULL;
+	set_commit_tree(c, NULL);
 	c->index = 0;
 	free_commit_buffer(pool, c);
 	free_commit_list(c->parents);
@@ -406,7 +412,7 @@
 	if (get_oid_hex(bufptr + 5, &parent) < 0)
 		return error("bad tree pointer in commit %s",
 			     oid_to_hex(&item->object.oid));
-	item->maybe_tree = lookup_tree(r, &parent);
+	set_commit_tree(item, lookup_tree(r, &parent));
 	bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
 	pptr = &item->parents;
 
diff --git a/commit.h b/commit.h
index 42728c2..f5295ca 100644
--- a/commit.h
+++ b/commit.h
@@ -32,7 +32,7 @@
 
 	/*
 	 * If the commit is loaded from the commit-graph file, then this
-	 * member may be NULL. Only access it through get_commit_tree()
+	 * member may be NULL. Only access it through repo_get_commit_tree()
 	 * or get_commit_tree_oid().
 	 */
 	struct tree *maybe_tree;
@@ -89,6 +89,12 @@
 {
 	return repo_parse_commit_gently(r, item, 0);
 }
+
+static inline int parse_commit_no_graph(struct commit *commit)
+{
+	return repo_parse_commit_internal(the_repository, commit, 0, 0);
+}
+
 #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
 #define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use)
 #define parse_commit_gently(item, quiet) repo_parse_commit_gently(the_repository, item, quiet)
@@ -143,7 +149,8 @@
  */
 void free_commit_buffer(struct parsed_object_pool *pool, struct commit *);
 
-struct tree *get_commit_tree(const struct commit *);
+struct tree *repo_get_commit_tree(struct repository *, const struct commit *);
+#define get_commit_tree(c) repo_get_commit_tree(the_repository, c)
 struct object_id *get_commit_tree_oid(const struct commit *);
 
 /*
@@ -177,10 +184,10 @@
 
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 
-extern int has_non_ascii(const char *text);
-extern const char *logmsg_reencode(const struct commit *commit,
-				   char **commit_encoding,
-				   const char *output_encoding);
+int has_non_ascii(const char *text);
+const char *logmsg_reencode(const struct commit *commit,
+			    char **commit_encoding,
+			    const char *output_encoding);
 const char *repo_logmsg_reencode(struct repository *r,
 				 const struct commit *commit,
 				 char **commit_encoding,
@@ -189,7 +196,7 @@
 #define logmsg_reencode(c, enc, out) repo_logmsg_reencode(the_repository, c, enc, out)
 #endif
 
-extern const char *skip_blank_lines(const char *msg);
+const char *skip_blank_lines(const char *msg);
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
@@ -240,23 +247,27 @@
 
 struct oid_array;
 struct ref;
-extern int register_shallow(struct repository *r, const struct object_id *oid);
-extern int unregister_shallow(const struct object_id *oid);
-extern int for_each_commit_graft(each_commit_graft_fn, void *);
-extern int is_repository_shallow(struct repository *r);
-extern struct commit_list *get_shallow_commits(struct object_array *heads,
-		int depth, int shallow_flag, int not_shallow_flag);
-extern struct commit_list *get_shallow_commits_by_rev_list(
+int register_shallow(struct repository *r, const struct object_id *oid);
+int unregister_shallow(const struct object_id *oid);
+int for_each_commit_graft(each_commit_graft_fn, void *);
+int is_repository_shallow(struct repository *r);
+struct commit_list *get_shallow_commits(struct object_array *heads,
+					int depth, int shallow_flag, int not_shallow_flag);
+struct commit_list *get_shallow_commits_by_rev_list(
 		int ac, const char **av, int shallow_flag, int not_shallow_flag);
-extern void set_alternate_shallow_file(struct repository *r, const char *path, int override);
-extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
-				 const struct oid_array *extra);
-extern void setup_alternate_shallow(struct lock_file *shallow_lock,
-				    const char **alternate_shallow_file,
-				    const struct oid_array *extra);
-extern const char *setup_temporary_shallow(const struct oid_array *extra);
-extern void advertise_shallow_grafts(int);
+void set_alternate_shallow_file(struct repository *r, const char *path, int override);
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+			  const struct oid_array *extra);
+void setup_alternate_shallow(struct lock_file *shallow_lock,
+			     const char **alternate_shallow_file,
+			     const struct oid_array *extra);
+const char *setup_temporary_shallow(const struct oid_array *extra);
+void advertise_shallow_grafts(int);
 
+/*
+ * Initialize with prepare_shallow_info() or zero-initialize (equivalent to
+ * prepare_shallow_info with a NULL oid_array).
+ */
 struct shallow_info {
 	struct oid_array *shallow;
 	int *ours, nr_ours;
@@ -272,21 +283,21 @@
 	int nr_commits;
 };
 
-extern void prepare_shallow_info(struct shallow_info *, struct oid_array *);
-extern void clear_shallow_info(struct shallow_info *);
-extern void remove_nonexistent_theirs_shallow(struct shallow_info *);
-extern void assign_shallow_commits_to_refs(struct shallow_info *info,
-					   uint32_t **used,
-					   int *ref_status);
-extern int delayed_reachability_test(struct shallow_info *si, int c);
+void prepare_shallow_info(struct shallow_info *, struct oid_array *);
+void clear_shallow_info(struct shallow_info *);
+void remove_nonexistent_theirs_shallow(struct shallow_info *);
+void assign_shallow_commits_to_refs(struct shallow_info *info,
+				    uint32_t **used,
+				    int *ref_status);
+int delayed_reachability_test(struct shallow_info *si, int c);
 #define PRUNE_SHOW_ONLY 1
 #define PRUNE_QUICK 2
-extern void prune_shallow(unsigned options);
+void prune_shallow(unsigned options);
 extern struct trace_key trace_shallow;
 
-extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
-extern int run_add_interactive(const char *revision, const char *patch_mode,
-			       const struct pathspec *pathspec);
+int interactive_add(int argc, const char **argv, const char *prefix, int patch);
+int run_add_interactive(const char *revision, const char *patch_mode,
+			const struct pathspec *pathspec);
 
 struct commit_extra_header {
 	struct commit_extra_header *next;
@@ -295,24 +306,24 @@
 	size_t len;
 };
 
-extern void append_merge_tag_headers(struct commit_list *parents,
-				     struct commit_extra_header ***tail);
+void append_merge_tag_headers(struct commit_list *parents,
+			      struct commit_extra_header ***tail);
 
-extern int commit_tree(const char *msg, size_t msg_len,
-		       const struct object_id *tree,
-		       struct commit_list *parents, struct object_id *ret,
-		       const char *author, const char *sign_commit);
+int commit_tree(const char *msg, size_t msg_len,
+		const struct object_id *tree,
+		struct commit_list *parents, struct object_id *ret,
+		const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const char *msg, size_t msg_len,
-				const struct object_id *tree,
-				struct commit_list *parents,
-				struct object_id *ret, const char *author,
-				const char *sign_commit,
-				struct commit_extra_header *);
+int commit_tree_extended(const char *msg, size_t msg_len,
+			 const struct object_id *tree,
+			 struct commit_list *parents,
+			 struct object_id *ret, const char *author,
+			 const char *sign_commit,
+			 struct commit_extra_header *);
 
-extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
+struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
 
-extern void free_commit_extra_headers(struct commit_extra_header *extra);
+void free_commit_extra_headers(struct commit_extra_header *extra);
 
 /*
  * Search the commit object contents given by "msg" for the header "key".
@@ -322,24 +333,24 @@
  * Note that some headers (like mergetag) may be multi-line. It is the caller's
  * responsibility to parse further in this case!
  */
-extern const char *find_commit_header(const char *msg, const char *key,
-				      size_t *out_len);
+const char *find_commit_header(const char *msg, const char *key,
+			       size_t *out_len);
 
 /* Find the end of the log message, the right place for a new trailer. */
-extern size_t ignore_non_trailer(const char *buf, size_t len);
+size_t ignore_non_trailer(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
-				 void *cb_data);
+				void *cb_data);
 
-extern int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
+int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data);
 
 struct merge_remote_desc {
 	struct object *obj; /* the named object, could be a tag */
 	char name[FLEX_ARRAY];
 };
-extern struct merge_remote_desc *merge_remote_util(struct commit *);
-extern void set_merge_remote_desc(struct commit *commit,
-				  const char *name, struct object *obj);
+struct merge_remote_desc *merge_remote_util(struct commit *);
+void set_merge_remote_desc(struct commit *commit,
+			   const char *name, struct object *obj);
 
 /*
  * Given "name" from the command line to merge, find the commit object
@@ -348,9 +359,9 @@
  */
 struct commit *get_merge_parent(const char *name);
 
-extern int parse_signed_commit(const struct commit *commit,
-			       struct strbuf *message, struct strbuf *signature);
-extern int remove_signature(struct strbuf *buf);
+int parse_signed_commit(const struct commit *commit,
+			struct strbuf *message, struct strbuf *signature);
+int remove_signature(struct strbuf *buf);
 
 /*
  * Check the signature of the given commit. The result of the check is stored
@@ -359,7 +370,7 @@
  * at all.  This may allocate memory for sig->gpg_output, sig->gpg_status,
  * sig->signer and sig->key.
  */
-extern int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
+int check_commit_signature(const struct commit *commit, struct signature_check *sigc);
 
 /* record author-date for each commit object */
 struct author_date_slab;
@@ -379,6 +390,6 @@
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);
 
 LAST_ARG_MUST_BE_NULL
-extern int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
+int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
 
 #endif /* COMMIT_H */
diff --git a/common-main.c b/common-main.c
index d484aec..582a7b1 100644
--- a/common-main.c
+++ b/common-main.c
@@ -27,6 +27,8 @@
 {
 	int result;
 
+	trace2_initialize_clock();
+
 	/*
 	 * Always open file descriptors 0/1/2 to avoid clobbering files
 	 * in die().  It also avoids messing up when the pipes are dup'ed
@@ -35,11 +37,11 @@
 	sanitize_stdfds();
 	restore_sigpipe_to_default();
 
+	git_resolve_executable_dir(argv[0]);
+
 	trace2_initialize();
 	trace2_cmd_start(argv);
-	trace2_collect_process_info();
-
-	git_resolve_executable_dir(argv[0]);
+	trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
 
 	git_setup_gettext();
 
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b04514..a2f74ac 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2569,6 +2569,8 @@
 	wchar_t **wenv, **wargv;
 	_startupinfo si;
 
+	trace2_initialize_clock();
+
 	maybe_redirect_std_handles();
 
 	/* get wide char arguments and environment */
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index 52bd620..8ccbd1c 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -1,5 +1,6 @@
 #include "../../cache.h"
 #include "../../json-writer.h"
+#include "lazyload.h"
 #include <Psapi.h>
 #include <tlHelp32.h>
 
@@ -137,11 +138,54 @@
 				   "windows/debugger_present", 1);
 }
 
-void trace2_collect_process_info(void)
+/*
+ * Emit JSON data with the peak memory usage of the current process.
+ */
+static void get_peak_memory_info(void)
+{
+	DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
+			  PPROCESS_MEMORY_COUNTERS, DWORD);
+
+	if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
+		PROCESS_MEMORY_COUNTERS pmc;
+
+		if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc,
+					 sizeof(pmc))) {
+			struct json_writer jw = JSON_WRITER_INIT;
+
+			jw_object_begin(&jw, 0);
+
+#define KV(kv) #kv, (intmax_t)pmc.kv
+
+			jw_object_intmax(&jw, KV(PageFaultCount));
+			jw_object_intmax(&jw, KV(PeakWorkingSetSize));
+			jw_object_intmax(&jw, KV(PeakPagefileUsage));
+
+			jw_end(&jw);
+
+			trace2_data_json("process", the_repository,
+					 "windows/memory", &jw);
+			jw_release(&jw);
+		}
+	}
+}
+
+void trace2_collect_process_info(enum trace2_process_info_reason reason)
 {
 	if (!trace2_is_enabled())
 		return;
 
-	get_is_being_debugged();
-	get_ancestry();
+	switch (reason) {
+	case TRACE2_PROCESS_INFO_STARTUP:
+		get_is_being_debugged();
+		get_ancestry();
+		return;
+
+	case TRACE2_PROCESS_INFO_EXIT:
+		get_peak_memory_info();
+		return;
+
+	default:
+		BUG("trace2_collect_process_info: unknown reason '%d'", reason);
+	}
 }
diff --git a/config.c b/config.c
index c2846df..296a6d9 100644
--- a/config.c
+++ b/config.c
@@ -1676,7 +1676,9 @@
 		repo_config = NULL;
 
 	current_parsing_scope = CONFIG_SCOPE_SYSTEM;
-	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
+	if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK,
+						  opts->system_gently ?
+						  ACCESS_EACCES_OK : 0))
 		ret += git_config_from_file(fn, git_etc_gitconfig(),
 					    data);
 
@@ -1688,14 +1690,15 @@
 		ret += git_config_from_file(fn, user_config, data);
 
 	current_parsing_scope = CONFIG_SCOPE_REPO;
-	if (repo_config && !access_or_die(repo_config, R_OK, 0))
+	if (!opts->ignore_repo && repo_config &&
+	    !access_or_die(repo_config, R_OK, 0))
 		ret += git_config_from_file(fn, repo_config, data);
 
 	/*
 	 * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
 	 * But let's not complicate things before it's actually needed.
 	 */
-	if (repository_format_worktree_config) {
+	if (!opts->ignore_worktree && repository_format_worktree_config) {
 		char *path = git_pathdup("config.worktree");
 		if (!access_or_die(path, R_OK, 0))
 			ret += git_config_from_file(fn, path, data);
@@ -1703,7 +1706,7 @@
 	}
 
 	current_parsing_scope = CONFIG_SCOPE_CMDLINE;
-	if (git_config_from_parameters(fn, data) < 0)
+	if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
 		die(_("unable to parse command-line config"));
 
 	current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
@@ -1794,6 +1797,23 @@
 	strbuf_release(&gitdir);
 }
 
+/*
+ * Read config but only enumerate system and global settings.
+ * Omit any repo-local, worktree-local, or command-line settings.
+ */
+void read_very_early_config(config_fn_t cb, void *data)
+{
+	struct config_options opts = { 0 };
+
+	opts.respect_includes = 1;
+	opts.ignore_repo = 1;
+	opts.ignore_worktree = 1;
+	opts.ignore_cmdline = 1;
+	opts.system_gently = 1;
+
+	config_with_options(cb, data, NULL, &opts);
+}
+
 static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
 {
 	struct config_set_element k;
@@ -2011,7 +2031,7 @@
 /* Functions use to read configuration from a repository */
 static void repo_read_config(struct repository *repo)
 {
-	struct config_options opts;
+	struct config_options opts = { 0 };
 
 	opts.respect_includes = 1;
 	opts.commondir = repo->commondir;
diff --git a/config.h b/config.h
index ee5d3fa..f0ed464 100644
--- a/config.h
+++ b/config.h
@@ -55,6 +55,10 @@
 
 struct config_options {
 	unsigned int respect_includes : 1;
+	unsigned int ignore_repo : 1;
+	unsigned int ignore_worktree : 1;
+	unsigned int ignore_cmdline : 1;
+	unsigned int system_gently : 1;
 	const char *commondir;
 	const char *git_dir;
 	config_parser_event_fn_t event_fn;
@@ -68,62 +72,63 @@
 };
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
-extern int git_default_config(const char *, const char *, void *);
-extern int git_config_from_file(config_fn_t fn, const char *, void *);
-extern int git_config_from_file_with_options(config_fn_t fn, const char *,
-					     void *,
-					     const struct config_options *);
-extern int git_config_from_mem(config_fn_t fn,
-			       const enum config_origin_type,
-			       const char *name,
-			       const char *buf, size_t len,
-			       void *data, const struct config_options *opts);
-extern int git_config_from_blob_oid(config_fn_t fn, const char *name,
-				    const struct object_id *oid, void *data);
-extern void git_config_push_parameter(const char *text);
-extern int git_config_from_parameters(config_fn_t fn, void *data);
-extern void read_early_config(config_fn_t cb, void *data);
-extern void git_config(config_fn_t fn, void *);
-extern int config_with_options(config_fn_t fn, void *,
-			       struct git_config_source *config_source,
-			       const struct config_options *opts);
-extern int git_parse_ssize_t(const char *, ssize_t *);
-extern int git_parse_ulong(const char *, unsigned long *);
-extern int git_parse_maybe_bool(const char *);
-extern int git_config_int(const char *, const char *);
-extern int64_t git_config_int64(const char *, const char *);
-extern unsigned long git_config_ulong(const char *, const char *);
-extern ssize_t git_config_ssize_t(const char *, const char *);
-extern int git_config_bool_or_int(const char *, const char *, int *);
-extern int git_config_bool(const char *, const char *);
-extern int git_config_string(const char **, const char *, const char *);
-extern int git_config_pathname(const char **, const char *, const char *);
-extern int git_config_expiry_date(timestamp_t *, const char *, const char *);
-extern int git_config_color(char *, const char *, const char *);
-extern int git_config_set_in_file_gently(const char *, const char *, const char *);
-extern void git_config_set_in_file(const char *, const char *, const char *);
-extern int git_config_set_gently(const char *, const char *);
-extern void git_config_set(const char *, const char *);
-extern int git_config_parse_key(const char *, char **, int *);
-extern int git_config_key_is_valid(const char *key);
-extern int git_config_set_multivar_gently(const char *, const char *, const char *, int);
-extern void git_config_set_multivar(const char *, const char *, const char *, int);
-extern int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
-extern void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
-extern int git_config_rename_section(const char *, const char *);
-extern int git_config_rename_section_in_file(const char *, const char *, const char *);
-extern int git_config_copy_section(const char *, const char *);
-extern int git_config_copy_section_in_file(const char *, const char *, const char *);
-extern const char *git_etc_gitconfig(void);
-extern int git_env_bool(const char *, int);
-extern unsigned long git_env_ulong(const char *, unsigned long);
-extern int git_config_system(void);
-extern int config_error_nonbool(const char *);
+int git_default_config(const char *, const char *, void *);
+int git_config_from_file(config_fn_t fn, const char *, void *);
+int git_config_from_file_with_options(config_fn_t fn, const char *,
+				      void *,
+				      const struct config_options *);
+int git_config_from_mem(config_fn_t fn,
+			const enum config_origin_type,
+			const char *name,
+			const char *buf, size_t len,
+			void *data, const struct config_options *opts);
+int git_config_from_blob_oid(config_fn_t fn, const char *name,
+			     const struct object_id *oid, void *data);
+void git_config_push_parameter(const char *text);
+int git_config_from_parameters(config_fn_t fn, void *data);
+void read_early_config(config_fn_t cb, void *data);
+void read_very_early_config(config_fn_t cb, void *data);
+void git_config(config_fn_t fn, void *);
+int config_with_options(config_fn_t fn, void *,
+			struct git_config_source *config_source,
+			const struct config_options *opts);
+int git_parse_ssize_t(const char *, ssize_t *);
+int git_parse_ulong(const char *, unsigned long *);
+int git_parse_maybe_bool(const char *);
+int git_config_int(const char *, const char *);
+int64_t git_config_int64(const char *, const char *);
+unsigned long git_config_ulong(const char *, const char *);
+ssize_t git_config_ssize_t(const char *, const char *);
+int git_config_bool_or_int(const char *, const char *, int *);
+int git_config_bool(const char *, const char *);
+int git_config_string(const char **, const char *, const char *);
+int git_config_pathname(const char **, const char *, const char *);
+int git_config_expiry_date(timestamp_t *, const char *, const char *);
+int git_config_color(char *, const char *, const char *);
+int git_config_set_in_file_gently(const char *, const char *, const char *);
+void git_config_set_in_file(const char *, const char *, const char *);
+int git_config_set_gently(const char *, const char *);
+void git_config_set(const char *, const char *);
+int git_config_parse_key(const char *, char **, int *);
+int git_config_key_is_valid(const char *key);
+int git_config_set_multivar_gently(const char *, const char *, const char *, int);
+void git_config_set_multivar(const char *, const char *, const char *, int);
+int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, int);
+void git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
+int git_config_rename_section(const char *, const char *);
+int git_config_rename_section_in_file(const char *, const char *, const char *);
+int git_config_copy_section(const char *, const char *);
+int git_config_copy_section_in_file(const char *, const char *, const char *);
+const char *git_etc_gitconfig(void);
+int git_env_bool(const char *, int);
+unsigned long git_env_ulong(const char *, unsigned long);
+int git_config_system(void);
+int config_error_nonbool(const char *);
 #if defined(__GNUC__)
 #define config_error_nonbool(s) (config_error_nonbool(s), const_error())
 #endif
 
-extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
+int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
 enum config_scope {
 	CONFIG_SCOPE_UNKNOWN = 0,
@@ -133,9 +138,9 @@
 	CONFIG_SCOPE_CMDLINE,
 };
 
-extern enum config_scope current_config_scope(void);
-extern const char *current_config_origin_type(void);
-extern const char *current_config_name(void);
+enum config_scope current_config_scope(void);
+const char *current_config_origin_type(void);
+const char *current_config_name(void);
 
 struct config_include_data {
 	int depth;
@@ -144,7 +149,7 @@
 	const struct config_options *opts;
 };
 #define CONFIG_INCLUDE_INIT { 0 }
-extern int git_config_include(const char *name, const char *value, void *data);
+int git_config_include(const char *name, const char *value, void *data);
 
 /*
  * Match and parse a config key of the form:
@@ -159,10 +164,10 @@
  * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
  * there is no subsection at all.
  */
-extern int parse_config_key(const char *var,
-			    const char *section,
-			    const char **subsection, int *subsection_len,
-			    const char **key);
+int parse_config_key(const char *var,
+		     const char *section,
+		     const char **subsection, int *subsection_len,
+		     const char **key);
 
 struct config_set_element {
 	struct hashmap_entry ent;
@@ -192,71 +197,71 @@
 	struct configset_list list;
 };
 
-extern void git_configset_init(struct config_set *cs);
-extern int git_configset_add_file(struct config_set *cs, const char *filename);
-extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
-extern void git_configset_clear(struct config_set *cs);
+void git_configset_init(struct config_set *cs);
+int git_configset_add_file(struct config_set *cs, const char *filename);
+const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+void git_configset_clear(struct config_set *cs);
 
 /*
  * These functions return 1 if not found, and 0 if found, leaving the found
  * value in the 'dest' pointer.
  */
-extern int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
-extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
-extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
-extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
-extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
-extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
+int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
+int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
+int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
 
 /* Functions for reading a repository's config */
 struct repository;
-extern void repo_config(struct repository *repo, config_fn_t fn, void *data);
-extern int repo_config_get_value(struct repository *repo,
-				 const char *key, const char **value);
-extern const struct string_list *repo_config_get_value_multi(struct repository *repo,
-							     const char *key);
-extern int repo_config_get_string_const(struct repository *repo,
-					const char *key, const char **dest);
-extern int repo_config_get_string(struct repository *repo,
-				  const char *key, char **dest);
-extern int repo_config_get_int(struct repository *repo,
+void repo_config(struct repository *repo, config_fn_t fn, void *data);
+int repo_config_get_value(struct repository *repo,
+			  const char *key, const char **value);
+const struct string_list *repo_config_get_value_multi(struct repository *repo,
+						      const char *key);
+int repo_config_get_string_const(struct repository *repo,
+				 const char *key, const char **dest);
+int repo_config_get_string(struct repository *repo,
+			   const char *key, char **dest);
+int repo_config_get_int(struct repository *repo,
+			const char *key, int *dest);
+int repo_config_get_ulong(struct repository *repo,
+			  const char *key, unsigned long *dest);
+int repo_config_get_bool(struct repository *repo,
+			 const char *key, int *dest);
+int repo_config_get_bool_or_int(struct repository *repo,
+				const char *key, int *is_bool, int *dest);
+int repo_config_get_maybe_bool(struct repository *repo,
 			       const char *key, int *dest);
-extern int repo_config_get_ulong(struct repository *repo,
-				 const char *key, unsigned long *dest);
-extern int repo_config_get_bool(struct repository *repo,
-				const char *key, int *dest);
-extern int repo_config_get_bool_or_int(struct repository *repo,
-				       const char *key, int *is_bool, int *dest);
-extern int repo_config_get_maybe_bool(struct repository *repo,
-				      const char *key, int *dest);
-extern int repo_config_get_pathname(struct repository *repo,
-				    const char *key, const char **dest);
+int repo_config_get_pathname(struct repository *repo,
+			     const char *key, const char **dest);
 
-extern int git_config_get_value(const char *key, const char **value);
-extern const struct string_list *git_config_get_value_multi(const char *key);
-extern void git_config_clear(void);
-extern int git_config_get_string_const(const char *key, const char **dest);
-extern int git_config_get_string(const char *key, char **dest);
-extern int git_config_get_int(const char *key, int *dest);
-extern int git_config_get_ulong(const char *key, unsigned long *dest);
-extern int git_config_get_bool(const char *key, int *dest);
-extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
-extern int git_config_get_maybe_bool(const char *key, int *dest);
-extern int git_config_get_pathname(const char *key, const char **dest);
-extern int git_config_get_index_threads(int *dest);
-extern int git_config_get_untracked_cache(void);
-extern int git_config_get_split_index(void);
-extern int git_config_get_max_percent_split_change(void);
-extern int git_config_get_fsmonitor(void);
+int git_config_get_value(const char *key, const char **value);
+const struct string_list *git_config_get_value_multi(const char *key);
+void git_config_clear(void);
+int git_config_get_string_const(const char *key, const char **dest);
+int git_config_get_string(const char *key, char **dest);
+int git_config_get_int(const char *key, int *dest);
+int git_config_get_ulong(const char *key, unsigned long *dest);
+int git_config_get_bool(const char *key, int *dest);
+int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
+int git_config_get_maybe_bool(const char *key, int *dest);
+int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_index_threads(int *dest);
+int git_config_get_untracked_cache(void);
+int git_config_get_split_index(void);
+int git_config_get_max_percent_split_change(void);
+int git_config_get_fsmonitor(void);
 
 /* This dies if the configured or default date is in the future */
-extern int git_config_get_expiry(const char *key, const char **output);
+int git_config_get_expiry(const char *key, const char **output);
 
 /* parse either "this many days" integer, or "5.days.ago" approxidate */
-extern int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
+int git_config_get_expiry_in_days(const char *key, timestamp_t *, timestamp_t now);
 
 struct key_value_info {
 	const char *filename;
@@ -265,8 +270,8 @@
 	enum config_scope scope;
 };
 
-extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
-extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
+NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
+NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 
 #define LOOKUP_CONFIG(mapping, var) \
 	lookup_config(mapping, ARRAY_SIZE(mapping), var)
diff --git a/config.mak.uname b/config.mak.uname
index 41e85fa..d916d1d 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -114,6 +114,8 @@
 	HAVE_BSD_SYSCTL = YesPlease
 	FREAD_READS_DIRECTORIES = UnfortunatelyYes
 	HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
+	BASIC_CFLAGS += -I/usr/local/include
+	BASIC_LDFLAGS += -L/usr/local/lib
 endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
@@ -269,6 +271,7 @@
 	INTERNAL_QSORT = UnfortunatelyYes
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -D_LARGE_FILES
+	FILENO_IS_A_MACRO = UnfortunatelyYes
 	ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
 		NO_PTHREADS = YesPlease
 	else
@@ -392,6 +395,7 @@
 	CFLAGS =
 	BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
 	COMPAT_OBJS = compat/msvc.o compat/winansi.o \
+		compat/win32/path-utils.o \
 		compat/win32/pthread.o compat/win32/syslog.o \
 		compat/win32/trace2_win32_process_info.o \
 		compat/win32/dirent.o
diff --git a/configure.ac b/configure.ac
index e0d0da3..be3b55f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -763,9 +763,19 @@
 GIT_CONF_SUBST([NEEDS_LIBGEN])
 test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen"
 
-AC_CHECK_LIB([c], [gettext],
-[LIBC_CONTAINS_LIBINTL=YesPlease],
-[LIBC_CONTAINS_LIBINTL=])
+AC_DEFUN([LIBINTL_SRC], [
+AC_LANG_PROGRAM([[
+#include <libintl.h>
+]],[[
+char *msg = gettext("test");
+]])])
+
+AC_MSG_CHECKING([if libc contains libintl])
+AC_LINK_IFELSE([LIBINTL_SRC],
+	[AC_MSG_RESULT([yes])
+	LIBC_CONTAINS_LIBINTL=YesPlease],
+	[AC_MSG_RESULT([no])
+	LIBC_CONTAINS_LIBINTL=])
 GIT_CONF_SUBST([LIBC_CONTAINS_LIBINTL])
 
 #
diff --git a/connect.h b/connect.h
index 32aff60..5f2382e 100644
--- a/connect.h
+++ b/connect.h
@@ -7,19 +7,19 @@
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
 #define CONNECT_IPV6          (1u << 3)
-extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
-extern int finish_connect(struct child_process *conn);
-extern int git_connection_is_socket(struct child_process *conn);
-extern int server_supports(const char *feature);
-extern int parse_feature_request(const char *features, const char *feature);
-extern const char *server_feature_value(const char *feature, int *len_ret);
-extern int url_is_local_not_ssh(const char *url);
+struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+int finish_connect(struct child_process *conn);
+int git_connection_is_socket(struct child_process *conn);
+int server_supports(const char *feature);
+int parse_feature_request(const char *features, const char *feature);
+const char *server_feature_value(const char *feature, int *len_ret);
+int url_is_local_not_ssh(const char *url);
 
 struct packet_reader;
-extern enum protocol_version discover_version(struct packet_reader *reader);
+enum protocol_version discover_version(struct packet_reader *reader);
 
-extern int server_supports_v2(const char *c, int die_on_error);
-extern int server_supports_feature(const char *c, const char *feature,
-				   int die_on_error);
+int server_supports_v2(const char *c, int die_on_error);
+int server_supports_feature(const char *c, const char *feature,
+			    int die_on_error);
 
 #endif
diff --git a/connected.c b/connected.c
index 1bba888..1ab481f 100644
--- a/connected.c
+++ b/connected.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "object-store.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
@@ -49,6 +50,22 @@
 		strbuf_release(&idx_file);
 	}
 
+	if (opt->check_refs_only) {
+		/*
+		 * For partial clones, we don't want to have to do a regular
+		 * connectivity check because we have to enumerate and exclude
+		 * all promisor objects (slow), and then the connectivity check
+		 * itself becomes a no-op because in a partial clone every
+		 * object is a promisor object. Instead, just make sure we
+		 * received the objects pointed to by each wanted ref.
+		 */
+		do {
+			if (!repo_has_object_file(the_repository, &oid))
+				return 1;
+		} while (!fn(cb_data, &oid));
+		return 0;
+	}
+
 	if (opt->shallow_file) {
 		argv_array_push(&rev_list.args, "--shallow-file");
 		argv_array_push(&rev_list.args, opt->shallow_file);
diff --git a/connected.h b/connected.h
index 8d5a6b3..ce2e7d8 100644
--- a/connected.h
+++ b/connected.h
@@ -46,6 +46,14 @@
 	 * during a fetch.
 	 */
 	unsigned is_deepening_fetch : 1;
+
+	/*
+	 * If non-zero, only check the top-level objects referenced by the
+	 * wanted refs (passed in as cb_data). This is useful for partial
+	 * clones, where enumerating and excluding all promisor objects is very
+	 * slow and the commit-walk itself becomes a no-op.
+	 */
+	unsigned check_refs_only : 1;
 };
 
 #define CHECK_CONNECTED_INIT { 0 }
diff --git a/contrib/coccinelle/commit.cocci b/contrib/coccinelle/commit.cocci
index c49aa55..d034533 100644
--- a/contrib/coccinelle/commit.cocci
+++ b/contrib/coccinelle/commit.cocci
@@ -10,19 +10,25 @@
 - c->maybe_tree->object.oid.hash
 + get_commit_tree_oid(c)->hash
 
-// These excluded functions must access c->maybe_tree direcly.
 @@
-identifier f !~ "^(get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit)$";
+identifier f !~ "^set_commit_tree$";
+expression c;
+expression s;
+@@
+  f(...) {<...
+- c->maybe_tree = s
++ set_commit_tree(c, s)
+  ...>}
+
+// These excluded functions must access c->maybe_tree direcly.
+// Note that if c->maybe_tree is written somewhere outside of these
+// functions, then the recommended transformation will be bogus with
+// repo_get_commit_tree() on the LHS.
+@@
+identifier f !~ "^(repo_get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit|set_commit_tree)$";
 expression c;
 @@
   f(...) {<...
 - c->maybe_tree
-+ get_commit_tree(c)
++ repo_get_commit_tree(specify_the_right_repo_here, c)
   ...>}
-
-@@
-expression c;
-expression s;
-@@
-- get_commit_tree(c) = s
-+ c->maybe_tree = s
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 42a3c8e..3eefbab 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2611,7 +2611,7 @@
 {
 	__git_has_doubledash && return
 
-	local subcommands="add status init deinit update summary foreach sync absorbgitdirs"
+	local subcommands="add status init deinit update set-branch summary foreach sync absorbgitdirs"
 	local subcommand="$(__git_find_on_cmdline "$subcommands")"
 	if [ -z "$subcommand" ]; then
 		case "$cur" in
@@ -2642,6 +2642,9 @@
 			--force --rebase --merge --reference --depth --recursive --jobs
 		"
 		;;
+	set-branch,--*)
+		__gitcomp "--default --branch"
+		;;
 	summary,--*)
 		__gitcomp "--cached --files --summary-limit"
 		;;
diff --git a/csum-file.h b/csum-file.h
index 3bf7184..a98b1ee 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -25,22 +25,22 @@
 	git_hash_ctx ctx;
 };
 
-extern void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *);
-extern int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
+void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *);
+int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
 
 /* finalize_hashfile flags */
 #define CSUM_CLOSE		1
 #define CSUM_FSYNC		2
 #define CSUM_HASH_IN_STREAM	4
 
-extern struct hashfile *hashfd(int fd, const char *name);
-extern struct hashfile *hashfd_check(const char *name);
-extern struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
-extern int finalize_hashfile(struct hashfile *, unsigned char *, unsigned int);
-extern void hashwrite(struct hashfile *, const void *, unsigned int);
-extern void hashflush(struct hashfile *f);
-extern void crc32_begin(struct hashfile *);
-extern uint32_t crc32_end(struct hashfile *);
+struct hashfile *hashfd(int fd, const char *name);
+struct hashfile *hashfd_check(const char *name);
+struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
+int finalize_hashfile(struct hashfile *, unsigned char *, unsigned int);
+void hashwrite(struct hashfile *, const void *, unsigned int);
+void hashflush(struct hashfile *f);
+void crc32_begin(struct hashfile *);
+uint32_t crc32_end(struct hashfile *);
 
 static inline void hashwrite_u8(struct hashfile *f, uint8_t data)
 {
diff --git a/decorate.h b/decorate.h
index 9014c1e..ee43dee 100644
--- a/decorate.h
+++ b/decorate.h
@@ -50,12 +50,12 @@
  * NULL), returning the previously associated pointer. If there is no previous
  * association, this function returns NULL.
  */
-extern void *add_decoration(struct decoration *n, const struct object *obj, void *decoration);
+void *add_decoration(struct decoration *n, const struct object *obj, void *decoration);
 
 /*
  * Return the pointer associated to the given object. If there is no
  * association, this function returns NULL.
  */
-extern void *lookup_decoration(struct decoration *n, const struct object *obj);
+void *lookup_decoration(struct decoration *n, const struct object *obj);
 
 #endif
diff --git a/delta.h b/delta.h
index 9b67531..2df5fe1 100644
--- a/delta.h
+++ b/delta.h
@@ -13,7 +13,7 @@
  * before free_delta_index() is called.  The returned pointer must be freed
  * using free_delta_index().
  */
-extern struct delta_index *
+struct delta_index *
 create_delta_index(const void *buf, unsigned long bufsize);
 
 /*
@@ -21,14 +21,14 @@
  *
  * Given pointer must be what create_delta_index() returned, or NULL.
  */
-extern void free_delta_index(struct delta_index *index);
+void free_delta_index(struct delta_index *index);
 
 /*
  * sizeof_delta_index: returns memory usage of delta index
  *
  * Given pointer must be what create_delta_index() returned, or NULL.
  */
-extern unsigned long sizeof_delta_index(struct delta_index *index);
+unsigned long sizeof_delta_index(struct delta_index *index);
 
 /*
  * create_delta: create a delta from given index for the given buffer
@@ -40,7 +40,7 @@
  * returned and *delta_size is updated with its size.  The returned buffer
  * must be freed by the caller.
  */
-extern void *
+void *
 create_delta(const struct delta_index *index,
 	     const void *buf, unsigned long bufsize,
 	     unsigned long *delta_size, unsigned long max_delta_size);
@@ -75,9 +75,9 @@
  * *trg_bufsize is updated with its size.  On failure a NULL pointer is
  * returned.  The returned buffer must be freed by the caller.
  */
-extern void *patch_delta(const void *src_buf, unsigned long src_size,
-			 const void *delta_buf, unsigned long delta_size,
-			 unsigned long *dst_size);
+void *patch_delta(const void *src_buf, unsigned long src_size,
+		  const void *delta_buf, unsigned long delta_size,
+		  unsigned long *dst_size);
 
 /* the smallest possible delta size is 4 bytes */
 #define DELTA_SIZE_MIN	4
diff --git a/diff-no-index.c b/diff-no-index.c
index 6001bae..7814eab 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -14,6 +14,7 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "parse-options.h"
 #include "string-list.h"
 #include "dir.h"
 
@@ -233,29 +234,37 @@
 	}
 }
 
-void diff_no_index(struct rev_info *revs,
-		   int argc, const char **argv)
+static const char * const diff_no_index_usage[] = {
+	N_("git diff --no-index [<options>] <path> <path>"),
+	NULL
+};
+
+int diff_no_index(struct rev_info *revs,
+		  int implicit_no_index,
+		  int argc, const char **argv)
 {
-	int i;
+	int i, no_index;
 	const char *paths[2];
 	struct strbuf replacement = STRBUF_INIT;
 	const char *prefix = revs->prefix;
+	struct option no_index_options[] = {
+		OPT_BOOL_F(0, "no-index", &no_index, "",
+			   PARSE_OPT_NONEG | PARSE_OPT_HIDDEN),
+		OPT_END(),
+	};
+	struct option *options;
 
-	for (i = 1; i < argc - 2; ) {
-		int j;
-		if (!strcmp(argv[i], "--no-index"))
-			i++;
-		else if (!strcmp(argv[i], "--"))
-			i++;
-		else {
-			j = diff_opt_parse(&revs->diffopt, argv + i, argc - i,
-					   revs->prefix);
-			if (j <= 0)
-				die("invalid diff option/value: %s", argv[i]);
-			i += j;
-		}
+	options = parse_options_concat(no_index_options,
+				       revs->diffopt.parseopts);
+	argc = parse_options(argc, argv, revs->prefix, options,
+			     diff_no_index_usage, 0);
+	if (argc != 2) {
+		if (implicit_no_index)
+			warning(_("Not a git repository. Use --no-index to "
+				  "compare two paths outside a working tree"));
+		usage_with_options(diff_no_index_usage, options);
 	}
-
+	FREE_AND_NULL(options);
 	for (i = 0; i < 2; i++) {
 		const char *p = argv[argc - 2 + i];
 		if (!strcmp(p, "-"))
@@ -287,7 +296,7 @@
 	revs->diffopt.flags.exit_with_status = 1;
 
 	if (queue_diff(&revs->diffopt, paths[0], paths[1]))
-		exit(1);
+		return 1;
 	diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
@@ -298,5 +307,5 @@
 	 * The return code for --no-index imitates diff(1):
 	 * 0 = no changes, 1 = changes, else error
 	 */
-	exit(diff_result_code(&revs->diffopt, 0));
+	return diff_result_code(&revs->diffopt, 0);
 }
diff --git a/diff.c b/diff.c
index 6dfad79..4d3cf83 100644
--- a/diff.c
+++ b/diff.c
@@ -25,6 +25,7 @@
 #include "packfile.h"
 #include "parse-options.h"
 #include "help.h"
+#include "fetch-object.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -4640,66 +4641,6 @@
 	FREE_AND_NULL(options->parseopts);
 }
 
-static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
-{
-	char c, *eq;
-	int len;
-
-	if (*arg != '-')
-		return 0;
-	c = *++arg;
-	if (!c)
-		return 0;
-	if (c == arg_short) {
-		c = *++arg;
-		if (!c)
-			return 1;
-		if (val && isdigit(c)) {
-			char *end;
-			int n = strtoul(arg, &end, 10);
-			if (*end)
-				return 0;
-			*val = n;
-			return 1;
-		}
-		return 0;
-	}
-	if (c != '-')
-		return 0;
-	arg++;
-	eq = strchrnul(arg, '=');
-	len = eq - arg;
-	if (!len || strncmp(arg, arg_long, len))
-		return 0;
-	if (*eq) {
-		int n;
-		char *end;
-		if (!isdigit(*++eq))
-			return 0;
-		n = strtoul(eq, &end, 10);
-		if (*end)
-			return 0;
-		*val = n;
-	}
-	return 1;
-}
-
-static inline int short_opt(char opt, const char **argv,
-			    const char **optarg)
-{
-	const char *arg = argv[0];
-	if (arg[0] != '-' || arg[1] != opt)
-		return 0;
-	if (arg[2] != '\0') {
-		*optarg = arg + 2;
-		return 1;
-	}
-	if (!argv[1])
-		die("Option '%c' requires a value", opt);
-	*optarg = argv[1];
-	return 2;
-}
-
 int parse_long_opt(const char *opt, const char **argv,
 		   const char **optarg)
 {
@@ -4819,10 +4760,19 @@
 	return opt->filter & filter_bit[(int) status];
 }
 
-static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt)
+unsigned diff_filter_bit(char status)
 {
+	prepare_filter_bits();
+	return filter_bit[(int) status];
+}
+
+static int diff_opt_diff_filter(const struct option *option,
+				const char *optarg, int unset)
+{
+	struct diff_options *opt = option->value;
 	int i, optch;
 
+	BUG_ON_OPT_NEG(unset);
 	prepare_filter_bits();
 
 	/*
@@ -4853,7 +4803,8 @@
 
 		bit = (0 <= optch && optch <= 'Z') ? filter_bit[optch] : 0;
 		if (!bit)
-			return optarg[i];
+			return error(_("unknown change class '%c' in --diff-filter=%s"),
+				     optarg[i], optarg);
 		if (negate)
 			opt->filter &= ~bit;
 		else
@@ -4868,25 +4819,29 @@
 	*fmt |= DIFF_FORMAT_PATCH;
 }
 
-static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *arg)
+static int diff_opt_ws_error_highlight(const struct option *option,
+				       const char *arg, int unset)
 {
+	struct diff_options *opt = option->value;
 	int val = parse_ws_error_highlight(arg);
 
-	if (val < 0) {
-		error("unknown value after ws-error-highlight=%.*s",
-		      -1 - val, arg);
-		return 0;
-	}
+	BUG_ON_OPT_NEG(unset);
+	if (val < 0)
+		return error(_("unknown value after ws-error-highlight=%.*s"),
+			     -1 - val, arg);
 	opt->ws_error_highlight = val;
-	return 1;
+	return 0;
 }
 
-static int parse_objfind_opt(struct diff_options *opt, const char *arg)
+static int diff_opt_find_object(const struct option *option,
+				const char *arg, int unset)
 {
+	struct diff_options *opt = option->value;
 	struct object_id oid;
 
+	BUG_ON_OPT_NEG(unset);
 	if (get_oid(arg, &oid))
-		return error("unable to resolve '%s'", arg);
+		return error(_("unable to resolve '%s'"), arg);
 
 	if (!opt->objfind)
 		opt->objfind = xcalloc(1, sizeof(*opt->objfind));
@@ -4895,7 +4850,7 @@
 	opt->flags.recursive = 1;
 	opt->flags.tree_in_recursive = 1;
 	oidset_insert(opt->objfind, &oid);
-	return 1;
+	return 0;
 }
 
 static int diff_opt_anchored(const struct option *opt,
@@ -4960,6 +4915,45 @@
 	return 0;
 }
 
+static int diff_opt_color_moved(const struct option *opt,
+				const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	if (unset) {
+		options->color_moved = COLOR_MOVED_NO;
+	} else if (!arg) {
+		if (diff_color_moved_default)
+			options->color_moved = diff_color_moved_default;
+		if (options->color_moved == COLOR_MOVED_NO)
+			options->color_moved = COLOR_MOVED_DEFAULT;
+	} else {
+		int cm = parse_color_moved(arg);
+		if (cm < 0)
+			return error(_("bad --color-moved argument: %s"), arg);
+		options->color_moved = cm;
+	}
+	return 0;
+}
+
+static int diff_opt_color_moved_ws(const struct option *opt,
+				   const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+	unsigned cm;
+
+	if (unset) {
+		options->color_moved_ws_handling = 0;
+		return 0;
+	}
+
+	cm = parse_color_moved_ws(arg);
+	if (cm & COLOR_MOVED_WS_ERROR)
+		return error(_("invalid mode '%s' in --color-moved-ws"), arg);
+	options->color_moved_ws_handling = cm;
+	return 0;
+}
+
 static int diff_opt_color_words(const struct option *opt,
 				const char *arg, int unset)
 {
@@ -5085,6 +5079,30 @@
 	return 0;
 }
 
+static int diff_opt_line_prefix(const struct option *opt,
+				const char *optarg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	options->line_prefix = optarg;
+	options->line_prefix_length = strlen(options->line_prefix);
+	graph_setup_line_prefix(options);
+	return 0;
+}
+
+static int diff_opt_no_prefix(const struct option *opt,
+			      const char *optarg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	BUG_ON_OPT_ARG(optarg);
+	options->a_prefix = "";
+	options->b_prefix = "";
+	return 0;
+}
+
 static enum parse_opt_result diff_opt_output(struct parse_opt_ctx_t *ctx,
 					     const struct option *opt,
 					     const char *arg, int unset)
@@ -5122,6 +5140,28 @@
 	return 0;
 }
 
+static int diff_opt_pickaxe_regex(const struct option *opt,
+				  const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	options->pickaxe = arg;
+	options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
+	return 0;
+}
+
+static int diff_opt_pickaxe_string(const struct option *opt,
+				   const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	options->pickaxe = arg;
+	options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+	return 0;
+}
+
 static int diff_opt_relative(const struct option *opt,
 			     const char *arg, int unset)
 {
@@ -5301,6 +5341,28 @@
 			 N_("show full pre- and post-image object names on the \"index\" lines")),
 		OPT_COLOR_FLAG(0, "color", &options->use_color,
 			       N_("show colored diff")),
+		OPT_CALLBACK_F(0, "ws-error-highlight", options, N_("<kind>"),
+			       N_("highlight whitespace errors in the 'context', 'old' or 'new' lines in the diff"),
+			       PARSE_OPT_NONEG, diff_opt_ws_error_highlight),
+		OPT_SET_INT('z', NULL, &options->line_termination,
+			    N_("do not munge pathnames and use NULs as output field terminators in --raw or --numstat"),
+			    0),
+		OPT__ABBREV(&options->abbrev),
+		OPT_STRING_F(0, "src-prefix", &options->a_prefix, N_("<prefix>"),
+			     N_("show the given source prefix instead of \"a/\""),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "dst-prefix", &options->b_prefix, N_("<prefix>"),
+			     N_("show the given source prefix instead of \"b/\""),
+			     PARSE_OPT_NONEG),
+		OPT_CALLBACK_F(0, "line-prefix", options, N_("<prefix>"),
+			       N_("prepend an additional prefix to every line of output"),
+			       PARSE_OPT_NONEG, diff_opt_line_prefix),
+		OPT_CALLBACK_F(0, "no-prefix", options, NULL,
+			       N_("do not show any source or destination prefix"),
+			       PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
+		OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
+			      N_("show context between diff hunks up to the specified number of lines"),
+			      PARSE_OPT_NONEG),
 		OPT_CALLBACK_F(0, "output-indicator-new",
 			       &options->output_indicators[OUTPUT_INDICATOR_NEW],
 			       N_("<char>"),
@@ -5343,6 +5405,8 @@
 		OPT_CALLBACK_F(0, "follow", options, NULL,
 			       N_("continue listing the history of a file beyond renames"),
 			       PARSE_OPT_NOARG, diff_opt_follow),
+		OPT_INTEGER('l', NULL, &options->rename_limit,
+			    N_("prevent rename/copy detection if the number of rename/copy targets exceeds given limit")),
 
 		OPT_GROUP(N_("Diff algorithm options")),
 		OPT_BIT(0, "minimal", &options->xdl_opts,
@@ -5388,6 +5452,12 @@
 		OPT_CALLBACK_F(0, "color-words", options, N_("<regex>"),
 			       N_("equivalent to --word-diff=color --word-diff-regex=<regex>"),
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_color_words),
+		OPT_CALLBACK_F(0, "color-moved", options, N_("<mode>"),
+			       N_("move lines of code are colored differently"),
+			       PARSE_OPT_OPTARG, diff_opt_color_moved),
+		OPT_CALLBACK_F(0, "color-moved-ws", options, N_("<mode>"),
+			       N_("how white spaces are ignored in --color-moved"),
+			       0, diff_opt_color_moved_ws),
 
 		OPT_GROUP(N_("Diff other options")),
 		OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"),
@@ -5415,6 +5485,32 @@
 			       N_("specify how differences in submodules are shown"),
 			       PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
 			       diff_opt_submodule),
+		OPT_SET_INT_F(0, "ita-invisible-in-index", &options->ita_invisible_in_index,
+			      N_("hide 'git add -N' entries from the index"),
+			      1, PARSE_OPT_NONEG),
+		OPT_SET_INT_F(0, "ita-visible-in-index", &options->ita_invisible_in_index,
+			      N_("treat 'git add -N' entries as real in the index"),
+			      0, PARSE_OPT_NONEG),
+		OPT_CALLBACK_F('S', NULL, options, N_("<string>"),
+			       N_("look for differences that change the number of occurrences of the specified string"),
+			       0, diff_opt_pickaxe_string),
+		OPT_CALLBACK_F('G', NULL, options, N_("<regex>"),
+			       N_("look for differences that change the number of occurrences of the specified regex"),
+			       0, diff_opt_pickaxe_regex),
+		OPT_BIT_F(0, "pickaxe-all", &options->pickaxe_opts,
+			  N_("show all changes in the changeset with -S or -G"),
+			  DIFF_PICKAXE_ALL, PARSE_OPT_NONEG),
+		OPT_BIT_F(0, "pickaxe-regex", &options->pickaxe_opts,
+			  N_("treat <string> in -S as extended POSIX regular expression"),
+			  DIFF_PICKAXE_REGEX, PARSE_OPT_NONEG),
+		OPT_FILENAME('O', NULL, &options->orderfile,
+			     N_("control the order in which files appear in the output")),
+		OPT_CALLBACK_F(0, "find-object", options, N_("<object-id>"),
+			       N_("look for differences that change the number of occurrences of the specified object"),
+			       PARSE_OPT_NONEG, diff_opt_find_object),
+		OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"),
+			       N_("select files by diff type"),
+			       PARSE_OPT_NONEG, diff_opt_diff_filter),
 		{ OPTION_CALLBACK, 0, "output", options, N_("<file>"),
 		  N_("Output to a specific file"),
 		  PARSE_OPT_NONEG, NULL, 0, diff_opt_output },
@@ -5429,10 +5525,6 @@
 int diff_opt_parse(struct diff_options *options,
 		   const char **av, int ac, const char *prefix)
 {
-	const char *arg = av[0];
-	const char *optarg;
-	int argcount;
-
 	if (!prefix)
 		prefix = "";
 
@@ -5443,101 +5535,7 @@
 			   PARSE_OPT_ONE_SHOT |
 			   PARSE_OPT_STOP_AT_NON_OPTION);
 
-	if (ac)
-		return ac;
-
-	/* flags options */
-	if (!strcmp(arg, "--color-moved")) {
-		if (diff_color_moved_default)
-			options->color_moved = diff_color_moved_default;
-		if (options->color_moved == COLOR_MOVED_NO)
-			options->color_moved = COLOR_MOVED_DEFAULT;
-	} else if (!strcmp(arg, "--no-color-moved"))
-		options->color_moved = COLOR_MOVED_NO;
-	else if (skip_prefix(arg, "--color-moved=", &arg)) {
-		int cm = parse_color_moved(arg);
-		if (cm < 0)
-			return error("bad --color-moved argument: %s", arg);
-		options->color_moved = cm;
-	} else if (!strcmp(arg, "--no-color-moved-ws")) {
-		options->color_moved_ws_handling = 0;
-	} else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
-		unsigned cm = parse_color_moved_ws(arg);
-		if (cm & COLOR_MOVED_WS_ERROR)
-			return -1;
-		options->color_moved_ws_handling = cm;
-	} else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
-		return parse_ws_error_highlight_opt(options, arg);
-	else if (!strcmp(arg, "--ita-invisible-in-index"))
-		options->ita_invisible_in_index = 1;
-	else if (!strcmp(arg, "--ita-visible-in-index"))
-		options->ita_invisible_in_index = 0;
-
-	/* misc options */
-	else if (!strcmp(arg, "-z"))
-		options->line_termination = 0;
-	else if ((argcount = short_opt('l', av, &optarg))) {
-		options->rename_limit = strtoul(optarg, NULL, 10);
-		return argcount;
-	}
-	else if ((argcount = short_opt('S', av, &optarg))) {
-		options->pickaxe = optarg;
-		options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
-		return argcount;
-	} else if ((argcount = short_opt('G', av, &optarg))) {
-		options->pickaxe = optarg;
-		options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
-		return argcount;
-	}
-	else if (!strcmp(arg, "--pickaxe-all"))
-		options->pickaxe_opts |= DIFF_PICKAXE_ALL;
-	else if (!strcmp(arg, "--pickaxe-regex"))
-		options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
-	else if ((argcount = short_opt('O', av, &optarg))) {
-		options->orderfile = prefix_filename(prefix, optarg);
-		return argcount;
-	} else if (skip_prefix(arg, "--find-object=", &arg))
-		return parse_objfind_opt(options, arg);
-	else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
-		int offending = parse_diff_filter_opt(optarg, options);
-		if (offending)
-			die("unknown change class '%c' in --diff-filter=%s",
-			    offending, optarg);
-		return argcount;
-	}
-	else if (!strcmp(arg, "--no-abbrev"))
-		options->abbrev = 0;
-	else if (!strcmp(arg, "--abbrev"))
-		options->abbrev = DEFAULT_ABBREV;
-	else if (skip_prefix(arg, "--abbrev=", &arg)) {
-		options->abbrev = strtoul(arg, NULL, 10);
-		if (options->abbrev < MINIMUM_ABBREV)
-			options->abbrev = MINIMUM_ABBREV;
-		else if (the_hash_algo->hexsz < options->abbrev)
-			options->abbrev = the_hash_algo->hexsz;
-	}
-	else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
-		options->a_prefix = optarg;
-		return argcount;
-	}
-	else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) {
-		options->line_prefix = optarg;
-		options->line_prefix_length = strlen(options->line_prefix);
-		graph_setup_line_prefix(options);
-		return argcount;
-	}
-	else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
-		options->b_prefix = optarg;
-		return argcount;
-	}
-	else if (!strcmp(arg, "--no-prefix"))
-		options->a_prefix = options->b_prefix = "";
-	else if (opt_arg(arg, '\0', "inter-hunk-context",
-			 &options->interhunkcontext))
-		;
-	else
-		return 0;
-	return 1;
+	return ac;
 }
 
 int parse_rename_score(const char **cp_p)
@@ -6480,8 +6478,41 @@
 	QSORT(q->queue, q->nr, diffnamecmp);
 }
 
+static void add_if_missing(struct repository *r,
+			   struct oid_array *to_fetch,
+			   const struct diff_filespec *filespec)
+{
+	if (filespec && filespec->oid_valid &&
+	    oid_object_info_extended(r, &filespec->oid, NULL,
+				     OBJECT_INFO_FOR_PREFETCH))
+		oid_array_append(to_fetch, &filespec->oid);
+}
+
 void diffcore_std(struct diff_options *options)
 {
+	if (options->repo == the_repository &&
+	    repository_format_partial_clone) {
+		/*
+		 * Prefetch the diff pairs that are about to be flushed.
+		 */
+		int i;
+		struct diff_queue_struct *q = &diff_queued_diff;
+		struct oid_array to_fetch = OID_ARRAY_INIT;
+
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			add_if_missing(options->repo, &to_fetch, p->one);
+			add_if_missing(options->repo, &to_fetch, p->two);
+		}
+		if (to_fetch.nr)
+			/*
+			 * NEEDSWORK: Consider deduplicating the OIDs sent.
+			 */
+			fetch_objects(repository_format_partial_clone,
+				      to_fetch.oid, to_fetch.nr);
+		oid_array_clear(&to_fetch);
+	}
+
 	/* NOTE please keep the following in sync with diff_tree_combined() */
 	if (options->skip_stat_unmatch)
 		diffcore_skip_stat_unmatch(options);
diff --git a/diff.h b/diff.h
index c9db982..b20cbcc 100644
--- a/diff.h
+++ b/diff.h
@@ -233,6 +233,8 @@
 	struct option *parseopts;
 };
 
+unsigned diff_filter_bit(char status);
+
 void diff_emit_submodule_del(struct diff_options *o, const char *line);
 void diff_emit_submodule_add(struct diff_options *o, const char *line);
 void diff_emit_submodule_untracked(struct diff_options *o, const char *path);
@@ -438,7 +440,8 @@
 
 int diff_result_code(struct diff_options *, int);
 
-void diff_no_index(struct rev_info *, int, const char **);
+int diff_no_index(struct rev_info *,
+		  int implicit_no_index, int, const char **);
 
 int index_differs_from(struct repository *r, const char *def,
 		       const struct diff_flags *flags,
diff --git a/dir.c b/dir.c
index b2cabad..ba4a51c 100644
--- a/dir.c
+++ b/dir.c
@@ -502,8 +502,7 @@
 }
 
 int report_path_error(const char *ps_matched,
-		      const struct pathspec *pathspec,
-		      const char *prefix)
+		      const struct pathspec *pathspec)
 {
 	/*
 	 * Make sure all pathspec matched; otherwise it is an error.
@@ -1467,9 +1466,11 @@
 			return path_none;
 		}
 		if (!(dir->flags & DIR_NO_GITLINKS)) {
-			struct object_id oid;
-			if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
+			struct strbuf sb = STRBUF_INIT;
+			strbuf_addstr(&sb, dirname);
+			if (is_nonbare_repository_dir(&sb))
 				return exclude ? path_excluded : path_untracked;
+			strbuf_release(&sb);
 		}
 		return path_recurse;
 	}
@@ -2315,6 +2316,14 @@
 	return lstat(f, &sb) == 0;
 }
 
+int repo_file_exists(struct repository *repo, const char *path)
+{
+	if (repo != the_repository)
+		BUG("do not know how to check file existence in arbitrary repo");
+
+	return file_exists(path);
+}
+
 static int cmp_icase(char a, char b)
 {
 	if (a == b)
@@ -2545,13 +2554,9 @@
 	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_offset(x) offsetof(struct ondisk_untracked_cache, x)
-#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
 
 struct write_data {
 	int index;	   /* number of written untracked_cache_dir */
@@ -2634,20 +2639,21 @@
 	struct write_data wd;
 	unsigned char varbuf[16];
 	int varint_len;
-	size_t len = strlen(untracked->exclude_per_dir);
+	const unsigned hashsz = the_hash_algo->rawsz;
 
-	FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
+	ouc = xcalloc(1, sizeof(*ouc));
 	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.oid.hash);
-	hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
 	ouc->dir_flags = htonl(untracked->dir_flags);
 
 	varint_len = encode_varint(untracked->ident.len, varbuf);
 	strbuf_add(out, varbuf, varint_len);
 	strbuf_addbuf(out, &untracked->ident);
 
-	strbuf_add(out, ouc, ouc_size(len));
+	strbuf_add(out, ouc, sizeof(*ouc));
+	strbuf_add(out, untracked->ss_info_exclude.oid.hash, hashsz);
+	strbuf_add(out, untracked->ss_excludes_file.oid.hash, hashsz);
+	strbuf_add(out, untracked->exclude_per_dir, strlen(untracked->exclude_per_dir) + 1);
 	FREE_AND_NULL(ouc);
 
 	if (!untracked->root) {
@@ -2732,54 +2738,49 @@
 			struct read_data *rd)
 {
 	struct untracked_cache_dir ud, *untracked;
-	const unsigned char *next, *data = rd->data, *end = rd->end;
+	const unsigned char *data = rd->data, *end = rd->end;
+	const unsigned char *eos;
 	unsigned int value;
-	int i, len;
+	int i;
 
 	memset(&ud, 0, sizeof(ud));
 
-	next = data;
-	value = decode_varint(&next);
-	if (next > end)
+	value = decode_varint(&data);
+	if (data > end)
 		return -1;
 	ud.recurse	   = 1;
 	ud.untracked_alloc = value;
 	ud.untracked_nr	   = value;
 	if (ud.untracked_nr)
 		ALLOC_ARRAY(ud.untracked, ud.untracked_nr);
-	data = next;
 
-	next = data;
-	ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
-	if (next > end)
+	ud.dirs_alloc = ud.dirs_nr = decode_varint(&data);
+	if (data > end)
 		return -1;
 	ALLOC_ARRAY(ud.dirs, ud.dirs_nr);
-	data = next;
 
-	len = strlen((const char *)data);
-	next = data + len + 1;
-	if (next > rd->end)
+	eos = memchr(data, '\0', end - data);
+	if (!eos || eos == end)
 		return -1;
-	*untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len));
+
+	*untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), eos - data, 1));
 	memcpy(untracked, &ud, sizeof(ud));
-	memcpy(untracked->name, data, len + 1);
-	data = next;
+	memcpy(untracked->name, data, eos - data + 1);
+	data = eos + 1;
 
 	for (i = 0; i < untracked->untracked_nr; i++) {
-		len = strlen((const char *)data);
-		next = data + len + 1;
-		if (next > rd->end)
+		eos = memchr(data, '\0', end - data);
+		if (!eos || eos == end)
 			return -1;
-		untracked->untracked[i] = xstrdup((const char*)data);
-		data = next;
+		untracked->untracked[i] = xmemdupz(data, eos - data);
+		data = eos + 1;
 	}
 
 	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)
+		if (read_one_dir(untracked->dirs + i, rd) < 0)
 			return -1;
 	}
 	return 0;
@@ -2834,6 +2835,9 @@
 	int ident_len;
 	ssize_t len;
 	const char *exclude_per_dir;
+	const unsigned hashsz = the_hash_algo->rawsz;
+	const unsigned offset = sizeof(struct ondisk_untracked_cache);
+	const unsigned exclude_per_dir_offset = offset + 2 * hashsz;
 
 	if (sz <= 1 || end[-1] != '\0')
 		return NULL;
@@ -2845,7 +2849,7 @@
 	ident = (const char *)next;
 	next += ident_len;
 
-	if (next + ouc_size(0) > end)
+	if (next + exclude_per_dir_offset + 1 > end)
 		return NULL;
 
 	uc = xcalloc(1, sizeof(*uc));
@@ -2853,15 +2857,15 @@
 	strbuf_add(&uc->ident, ident, ident_len);
 	load_oid_stat(&uc->ss_info_exclude,
 		      next + ouc_offset(info_exclude_stat),
-		      next + ouc_offset(info_exclude_sha1));
+		      next + offset);
 	load_oid_stat(&uc->ss_excludes_file,
 		      next + ouc_offset(excludes_file_stat),
-		      next + ouc_offset(excludes_file_sha1));
+		      next + offset + hashsz);
 	uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
-	exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
+	exclude_per_dir = (const char *)next + exclude_per_dir_offset;
 	uc->exclude_per_dir = xstrdup(exclude_per_dir);
 	/* NUL after exclude_per_dir is covered by sizeof(*ouc) */
-	next += ouc_size(strlen(exclude_per_dir));
+	next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1;
 	if (next >= end)
 		goto done2;
 
diff --git a/dir.h b/dir.h
index e3ec261..680079b 100644
--- a/dir.h
+++ b/dir.h
@@ -202,7 +202,7 @@
 };
 
 /*Count the number of slashes for string s*/
-extern int count_slashes(const char *s);
+int count_slashes(const char *s);
 
 /*
  * The ordering of these constants is significant, with
@@ -213,27 +213,27 @@
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
-extern int simple_length(const char *match);
-extern int no_wildcard(const char *string);
-extern char *common_prefix(const struct pathspec *pathspec);
-extern int match_pathspec(const struct index_state *istate,
-			  const struct pathspec *pathspec,
-			  const char *name, int namelen,
-			  int prefix, char *seen, int is_dir);
-extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
-extern int within_depth(const char *name, int namelen, int depth, int max_depth);
+int simple_length(const char *match);
+int no_wildcard(const char *string);
+char *common_prefix(const struct pathspec *pathspec);
+int match_pathspec(const struct index_state *istate,
+		   const struct pathspec *pathspec,
+		   const char *name, int namelen,
+		   int prefix, char *seen, int is_dir);
+int report_path_error(const char *ps_matched, const struct pathspec *pathspec);
+int within_depth(const char *name, int namelen, int depth, int max_depth);
 
-extern int fill_directory(struct dir_struct *dir,
-			  struct index_state *istate,
-			  const struct pathspec *pathspec);
-extern int read_directory(struct dir_struct *, struct index_state *istate,
-			  const char *path, int len,
-			  const struct pathspec *pathspec);
+int fill_directory(struct dir_struct *dir,
+		   struct index_state *istate,
+		   const struct pathspec *pathspec);
+int read_directory(struct dir_struct *, struct index_state *istate,
+		   const char *path, int len,
+		   const struct pathspec *pathspec);
 
-extern int is_excluded_from_list(const char *pathname, int pathlen,
-				 const char *basename, int *dtype,
-				 struct exclude_list *el,
-				 struct index_state *istate);
+int is_excluded_from_list(const char *pathname, int pathlen,
+			  const char *basename, int *dtype,
+			  struct exclude_list *el,
+			  struct index_state *istate);
 struct dir_entry *dir_add_ignored(struct dir_struct *dir,
 				  struct index_state *istate,
 				  const char *pathname, int len);
@@ -242,37 +242,39 @@
  * these implement the matching logic for dir.c:excluded_from_list and
  * attr.c:path_matches()
  */
-extern int match_basename(const char *, int,
-			  const char *, int, int, unsigned);
-extern int match_pathname(const char *, int,
-			  const char *, int,
-			  const char *, int, int, unsigned);
+int match_basename(const char *, int,
+		   const char *, int, int, unsigned);
+int match_pathname(const char *, int,
+		   const char *, int,
+		   const char *, int, int, unsigned);
 
-extern struct exclude *last_exclude_matching(struct dir_struct *dir,
-					     struct index_state *istate,
-					     const char *name, int *dtype);
+struct exclude *last_exclude_matching(struct dir_struct *dir,
+				      struct index_state *istate,
+				      const char *name, int *dtype);
 
-extern int is_excluded(struct dir_struct *dir,
-		       struct index_state *istate,
-		       const char *name, int *dtype);
+int is_excluded(struct dir_struct *dir,
+		struct index_state *istate,
+		const char *name, int *dtype);
 
-extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
-					     int group_type, const char *src);
-extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
-					  struct exclude_list *el, struct  index_state *istate);
-extern void add_excludes_from_file(struct dir_struct *, const char *fname);
-extern int add_excludes_from_blob_to_list(struct object_id *oid,
-					  const char *base, int baselen,
-					  struct exclude_list *el);
-extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
-extern void add_exclude(const char *string, const char *base,
-			int baselen, struct exclude_list *el, int srcpos);
-extern void clear_exclude_list(struct exclude_list *el);
-extern void clear_directory(struct dir_struct *dir);
-extern int file_exists(const char *);
+struct exclude_list *add_exclude_list(struct dir_struct *dir,
+				      int group_type, const char *src);
+int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
+				   struct exclude_list *el, struct  index_state *istate);
+void add_excludes_from_file(struct dir_struct *, const char *fname);
+int add_excludes_from_blob_to_list(struct object_id *oid,
+				   const char *base, int baselen,
+				   struct exclude_list *el);
+void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
+void add_exclude(const char *string, const char *base,
+		 int baselen, struct exclude_list *el, int srcpos);
+void clear_exclude_list(struct exclude_list *el);
+void clear_directory(struct dir_struct *dir);
 
-extern int is_inside_dir(const char *dir);
-extern int dir_inside_of(const char *subdir, const char *dir);
+int repo_file_exists(struct repository *repo, const char *path);
+int file_exists(const char *);
+
+int is_inside_dir(const char *dir);
+int dir_inside_of(const char *subdir, const char *dir);
 
 static inline int is_dot_or_dotdot(const char *name)
 {
@@ -281,9 +283,9 @@
 		 (name[1] == '.' && name[2] == '\0')));
 }
 
-extern int is_empty_dir(const char *dir);
+int is_empty_dir(const char *dir);
 
-extern void setup_standard_excludes(struct dir_struct *dir);
+void setup_standard_excludes(struct dir_struct *dir);
 
 
 /* Constants for remove_dir_recursively: */
@@ -311,26 +313,26 @@
  * This function uses path as temporary scratch space, but restores it
  * before returning.
  */
-extern int remove_dir_recursively(struct strbuf *path, int flag);
+int remove_dir_recursively(struct strbuf *path, int flag);
 
 /* tries to remove the path with empty directories along it, ignores ENOENT */
-extern int remove_path(const char *path);
+int remove_path(const char *path);
 
-extern int fspathcmp(const char *a, const char *b);
-extern int fspathncmp(const char *a, const char *b, size_t count);
+int fspathcmp(const char *a, const char *b);
+int fspathncmp(const char *a, const char *b, size_t count);
 
 /*
  * The prefix part of pattern must not contains wildcards.
  */
 struct pathspec_item;
-extern int git_fnmatch(const struct pathspec_item *item,
-		       const char *pattern, const char *string,
-		       int prefix);
+int git_fnmatch(const struct pathspec_item *item,
+		const char *pattern, const char *string,
+		int prefix);
 
-extern int submodule_path_match(const struct index_state *istate,
-				const struct pathspec *ps,
-				const char *submodule_name,
-				char *seen);
+int submodule_path_match(const struct index_state *istate,
+			 const struct pathspec *ps,
+			 const char *submodule_name,
+			 char *seen);
 
 static inline int ce_path_match(const struct index_state *istate,
 				const struct cache_entry *ce,
@@ -372,10 +374,10 @@
  * When `recurse_into_nested` is set, recurse into any nested submodules,
  * connecting them as well.
  */
-extern void connect_work_tree_and_git_dir(const char *work_tree,
-					  const char *git_dir,
-					  int recurse_into_nested);
-extern void relocate_gitdir(const char *path,
-			    const char *old_git_dir,
-			    const char *new_git_dir);
+void connect_work_tree_and_git_dir(const char *work_tree,
+				   const char *git_dir,
+				   int recurse_into_nested);
+void relocate_gitdir(const char *path,
+		     const char *old_git_dir,
+		     const char *new_git_dir);
 #endif
diff --git a/exec-cmd.h b/exec-cmd.h
index 2522453..8cd1df2 100644
--- a/exec-cmd.h
+++ b/exec-cmd.h
@@ -3,14 +3,14 @@
 
 struct argv_array;
 
-extern void git_set_exec_path(const char *exec_path);
-extern void git_resolve_executable_dir(const char *path);
-extern const char *git_exec_path(void);
-extern void setup_path(void);
-extern const char **prepare_git_cmd(struct argv_array *out, const char **argv);
-extern int execv_git_cmd(const char **argv); /* NULL terminated */
+void git_set_exec_path(const char *exec_path);
+void git_resolve_executable_dir(const char *path);
+const char *git_exec_path(void);
+void setup_path(void);
+const char **prepare_git_cmd(struct argv_array *out, const char **argv);
+int execv_git_cmd(const char **argv); /* NULL terminated */
 LAST_ARG_MUST_BE_NULL
-extern int execl_git_cmd(const char *cmd, ...);
-extern char *system_path(const char *path);
+int execl_git_cmd(const char *cmd, ...);
+char *system_path(const char *path);
 
 #endif /* GIT_EXEC_CMD_H */
diff --git a/fast-import.c b/fast-import.c
index b7ba755..f38d04f 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -29,6 +29,13 @@
  */
 #define NO_DELTA S_ISUID
 
+/*
+ * The amount of additional space required in order to write an object into the
+ * current pack. This is the hash lengths at the end of the pack, plus the
+ * length of one object ID.
+ */
+#define PACK_SIZE_THRESHOLD (the_hash_algo->rawsz * 3)
+
 struct object_entry {
 	struct pack_idx_entry idx;
 	struct object_entry *next;
@@ -742,7 +749,8 @@
 	if (c != last)
 		die("internal consistency error creating the index");
 
-	tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts, pack_data->sha1);
+	tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts,
+				 pack_data->hash);
 	free(idx);
 	return tmpfile;
 }
@@ -753,7 +761,7 @@
 	struct strbuf name = STRBUF_INIT;
 	int keep_fd;
 
-	odb_pack_name(&name, pack_data->sha1, "keep");
+	odb_pack_name(&name, pack_data->hash, "keep");
 	keep_fd = odb_pack_keep(name.buf);
 	if (keep_fd < 0)
 		die_errno("cannot create keep file");
@@ -761,11 +769,11 @@
 	if (close(keep_fd))
 		die_errno("failed to write keep file");
 
-	odb_pack_name(&name, pack_data->sha1, "pack");
+	odb_pack_name(&name, pack_data->hash, "pack");
 	if (finalize_object_file(pack_data->pack_name, name.buf))
 		die("cannot store pack file");
 
-	odb_pack_name(&name, pack_data->sha1, "idx");
+	odb_pack_name(&name, pack_data->hash, "idx");
 	if (finalize_object_file(curr_index_name, name.buf))
 		die("cannot store index file");
 	free((void *)curr_index_name);
@@ -779,7 +787,7 @@
 
 	for (k = 0; k < pack_id; k++) {
 		struct packed_git *p = all_packs[k];
-		odb_pack_name(&name, p->sha1, "keep");
+		odb_pack_name(&name, p->hash, "keep");
 		unlink_or_warn(name.buf);
 	}
 	strbuf_release(&name);
@@ -821,9 +829,9 @@
 
 		close_pack_windows(pack_data);
 		finalize_hashfile(pack_file, cur_pack_oid.hash, 0);
-		fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
-				    pack_data->pack_name, object_count,
-				    cur_pack_oid.hash, pack_size);
+		fixup_pack_header_footer(pack_data->pack_fd, pack_data->hash,
+					 pack_data->pack_name, object_count,
+					 cur_pack_oid.hash, pack_size);
 
 		if (object_count <= unpack_limit) {
 			if (!loosen_small_pack(pack_data)) {
@@ -948,8 +956,9 @@
 	git_deflate_end(&s);
 
 	/* Determine if we should auto-checkpoint. */
-	if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
-		|| (pack_size + 60 + s.total_out) < pack_size) {
+	if ((max_packsize
+		&& (pack_size + PACK_SIZE_THRESHOLD + s.total_out) > max_packsize)
+		|| (pack_size + PACK_SIZE_THRESHOLD + s.total_out) < pack_size) {
 
 		/* This new object needs to *not* have the current pack_id. */
 		e->pack_id = pack_id + 1;
@@ -1044,8 +1053,9 @@
 	int status = Z_OK;
 
 	/* Determine if we should auto-checkpoint. */
-	if ((max_packsize && (pack_size + 60 + len) > max_packsize)
-		|| (pack_size + 60 + len) < pack_size)
+	if ((max_packsize
+		&& (pack_size + PACK_SIZE_THRESHOLD + len) > max_packsize)
+		|| (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size)
 		cycle_packfile();
 
 	hashfile_checkpoint(pack_file, &checkpoint);
@@ -1240,7 +1250,7 @@
 		c += e->name->str_len + 1;
 		hashcpy(e->versions[0].oid.hash, (unsigned char *)c);
 		hashcpy(e->versions[1].oid.hash, (unsigned char *)c);
-		c += GIT_SHA1_RAWSZ;
+		c += the_hash_algo->rawsz;
 	}
 	free(buf);
 }
@@ -1287,7 +1297,7 @@
 		strbuf_addf(b, "%o %s%c",
 			(unsigned int)(e->versions[v].mode & ~NO_DELTA),
 			e->name->str_dat, '\0');
-		strbuf_add(b, e->versions[v].oid.hash, GIT_SHA1_RAWSZ);
+		strbuf_add(b, e->versions[v].oid.hash, the_hash_algo->rawsz);
 	}
 }
 
@@ -1748,8 +1758,6 @@
 	}
 
 	for (;;) {
-		const char *p;
-
 		if (unread_command_buf) {
 			unread_command_buf = 0;
 		} else {
@@ -1782,14 +1790,6 @@
 			rc->prev->next = rc;
 			cmd_tail = rc;
 		}
-		if (skip_prefix(command_buf.buf, "get-mark ", &p)) {
-			parse_get_mark(p);
-			continue;
-		}
-		if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
-			parse_cat_blob(p);
-			continue;
-		}
 		if (command_buf.buf[0] == '#')
 			continue;
 		return 0;
@@ -2046,7 +2046,9 @@
 	unsigned int i, tmp_hex_oid_len, tmp_fullpath_len;
 	uintmax_t num_notes = 0;
 	struct object_id oid;
-	char realpath[60];
+	/* hex oid + '/' between each pair of hex digits + NUL */
+	char realpath[GIT_MAX_HEXSZ + ((GIT_MAX_HEXSZ / 2) - 1) + 1];
+	const unsigned hexsz = the_hash_algo->hexsz;
 
 	if (!root->tree)
 		load_tree(root);
@@ -2066,7 +2068,7 @@
 		 * of 2 chars.
 		 */
 		if (!e->versions[1].mode ||
-		    tmp_hex_oid_len > GIT_SHA1_HEXSZ ||
+		    tmp_hex_oid_len > hexsz ||
 		    e->name->str_len % 2)
 			continue;
 
@@ -2080,7 +2082,7 @@
 		tmp_fullpath_len += e->name->str_len;
 		fullpath[tmp_fullpath_len] = '\0';
 
-		if (tmp_hex_oid_len == GIT_SHA1_HEXSZ && !get_oid_hex(hex_oid, &oid)) {
+		if (tmp_hex_oid_len == hexsz && !get_oid_hex(hex_oid, &oid)) {
 			/* This is a note entry */
 			if (fanout == 0xff) {
 				/* Counting mode, no rename */
@@ -2254,8 +2256,15 @@
 			strbuf_addstr(&uq, p);
 			p = uq.buf;
 		}
-		read_next_command();
-		parse_and_store_blob(&last_blob, &oid, 0);
+		while (read_next_command() != EOF) {
+			const char *v;
+			if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+				parse_cat_blob(v);
+			else {
+				parse_and_store_blob(&last_blob, &oid, 0);
+				break;
+			}
+		}
 	} else {
 		enum object_type expected = S_ISDIR(mode) ?
 						OBJ_TREE: OBJ_BLOB;
@@ -2351,7 +2360,7 @@
 	struct object_entry *oe;
 	struct branch *s;
 	struct object_id oid, commit_oid;
-	char path[60];
+	char path[GIT_MAX_RAWSZ * 3];
 	uint16_t inline_data = 0;
 	unsigned char new_fanout;
 
@@ -2404,7 +2413,7 @@
 		char *buf = read_object_with_reference(&commit_oid,
 						       commit_type, &size,
 						       &commit_oid);
-		if (!buf || size < 46)
+		if (!buf || size < the_hash_algo->hexsz + 6)
 			die("Not a valid commit: %s", p);
 		free(buf);
 	} else
@@ -2455,7 +2464,7 @@
 
 static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
 {
-	if (!buf || size < GIT_SHA1_HEXSZ + 6)
+	if (!buf || size < the_hash_algo->hexsz + 6)
 		die("Not a valid commit: %s", oid_to_hex(&b->oid));
 	if (memcmp("tree ", buf, 5)
 		|| get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid))
@@ -2554,7 +2563,7 @@
 			char *buf = read_object_with_reference(&n->oid,
 							       commit_type,
 							       &size, &n->oid);
-			if (!buf || size < 46)
+			if (!buf || size < the_hash_algo->hexsz + 6)
 				die("Not a valid commit: %s", from);
 			free(buf);
 		} else
@@ -2627,6 +2636,8 @@
 			file_change_deleteall(b);
 		else if (skip_prefix(command_buf.buf, "ls ", &v))
 			parse_ls(v, b);
+		else if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+			parse_cat_blob(v);
 		else {
 			unread_command_buf = 1;
 			break;
@@ -2841,7 +2852,7 @@
 		die("Unknown mark: %s", command_buf.buf);
 
 	xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid));
-	cat_blob_write(output, GIT_SHA1_HEXSZ + 1);
+	cat_blob_write(output, the_hash_algo->hexsz + 1);
 }
 
 static void parse_cat_blob(const char *p)
@@ -2871,6 +2882,8 @@
 {
 	unsigned long size;
 	char *buf = NULL;
+	const unsigned hexsz = the_hash_algo->hexsz;
+
 	if (!oe) {
 		enum object_type type = oid_object_info(the_repository, oid,
 							NULL);
@@ -2904,12 +2917,12 @@
 	/* Peel one layer. */
 	switch (oe->type) {
 	case OBJ_TAG:
-		if (size < GIT_SHA1_HEXSZ + strlen("object ") ||
+		if (size < hexsz + strlen("object ") ||
 		    get_oid_hex(buf + strlen("object "), oid))
 			die("Invalid SHA1 in tag: %s", command_buf.buf);
 		break;
 	case OBJ_COMMIT:
-		if (size < GIT_SHA1_HEXSZ + strlen("tree ") ||
+		if (size < hexsz + strlen("tree ") ||
 		    get_oid_hex(buf + strlen("tree "), oid))
 			die("Invalid SHA1 in commit: %s", command_buf.buf);
 	}
@@ -2941,7 +2954,7 @@
 	return e;
 }
 
-static void print_ls(int mode, const unsigned char *sha1, const char *path)
+static void print_ls(int mode, const unsigned char *hash, const char *path)
 {
 	static struct strbuf line = STRBUF_INIT;
 
@@ -2961,7 +2974,7 @@
 		/* mode SP type SP object_name TAB path LF */
 		strbuf_reset(&line);
 		strbuf_addf(&line, "%06o %s %s\t",
-				mode & ~NO_DELTA, type, sha1_to_hex(sha1));
+				mode & ~NO_DELTA, type, hash_to_hex(hash));
 		quote_c_style(path, &line, NULL, 0);
 		strbuf_addch(&line, '\n');
 	}
@@ -3303,14 +3316,18 @@
 		const char *v;
 		if (!strcmp("blob", command_buf.buf))
 			parse_new_blob();
-		else if (skip_prefix(command_buf.buf, "ls ", &v))
-			parse_ls(v, NULL);
 		else if (skip_prefix(command_buf.buf, "commit ", &v))
 			parse_new_commit(v);
 		else if (skip_prefix(command_buf.buf, "tag ", &v))
 			parse_new_tag(v);
 		else if (skip_prefix(command_buf.buf, "reset ", &v))
 			parse_reset_branch(v);
+		else if (skip_prefix(command_buf.buf, "ls ", &v))
+			parse_ls(v, NULL);
+		else if (skip_prefix(command_buf.buf, "cat-blob ", &v))
+			parse_cat_blob(v);
+		else if (skip_prefix(command_buf.buf, "get-mark ", &v))
+			parse_get_mark(v);
 		else if (!strcmp("checkpoint", command_buf.buf))
 			parse_checkpoint();
 		else if (!strcmp("done", command_buf.buf))
diff --git a/fetch-pack.c b/fetch-pack.c
index e69993b..3f24d0c 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -573,9 +573,14 @@
 		next = ref->next;
 
 		if (starts_with(ref->name, "refs/") &&
-		    check_refname_format(ref->name, 0))
-			; /* trash */
-		else {
+		    check_refname_format(ref->name, 0)) {
+			/*
+			 * trash or a peeled value; do not even add it to
+			 * unmatched list
+			 */
+			free_one_ref(ref);
+			continue;
+		} else {
 			while (i < nr_sought) {
 				int cmp = strcmp(ref->name, sought[i]->name);
 				if (cmp < 0)
@@ -630,10 +635,7 @@
 	}
 
 	oidset_clear(&tip_oids);
-	for (ref = unmatched; ref; ref = next) {
-		next = ref->next;
-		free(ref);
-	}
+	free_refs(unmatched);
 
 	*refs = newlist;
 }
@@ -1253,9 +1255,11 @@
 }
 
 static void receive_shallow_info(struct fetch_pack_args *args,
-				 struct packet_reader *reader)
+				 struct packet_reader *reader,
+				 struct oid_array *shallows,
+				 struct shallow_info *si)
 {
-	int line_received = 0;
+	int unshallow_received = 0;
 
 	process_section_header(reader, "shallow-info", 0);
 	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
@@ -1265,8 +1269,7 @@
 		if (skip_prefix(reader->line, "shallow ", &arg)) {
 			if (get_oid_hex(arg, &oid))
 				die(_("invalid shallow line: %s"), reader->line);
-			register_shallow(the_repository, &oid);
-			line_received = 1;
+			oid_array_append(shallows, &oid);
 			continue;
 		}
 		if (skip_prefix(reader->line, "unshallow ", &arg)) {
@@ -1279,7 +1282,7 @@
 				die(_("error in object: %s"), reader->line);
 			if (unregister_shallow(&oid))
 				die(_("no shallow found: %s"), reader->line);
-			line_received = 1;
+			unshallow_received = 1;
 			continue;
 		}
 		die(_("expected shallow/unshallow, got %s"), reader->line);
@@ -1289,15 +1292,41 @@
 	    reader->status != PACKET_READ_DELIM)
 		die(_("error processing shallow info: %d"), reader->status);
 
-	if (line_received) {
+	if (args->deepen || unshallow_received) {
+		/*
+		 * Treat these as shallow lines caused by our depth settings.
+		 * In v0, these lines cannot cause refs to be rejected; do the
+		 * same.
+		 */
+		int i;
+
+		for (i = 0; i < shallows->nr; i++)
+			register_shallow(the_repository, &shallows->oid[i]);
 		setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
 					NULL);
 		args->deepen = 1;
+	} else if (shallows->nr) {
+		/*
+		 * Treat these as shallow lines caused by the remote being
+		 * shallow. In v0, remote refs that reach these objects are
+		 * rejected (unless --update-shallow is set); do the same.
+		 */
+		prepare_shallow_info(si, shallows);
+		if (si->nr_ours || si->nr_theirs)
+			alternate_shallow_file =
+				setup_temporary_shallow(si->shallow);
+		else
+			alternate_shallow_file = NULL;
 	} else {
 		alternate_shallow_file = NULL;
 	}
 }
 
+static int cmp_name_ref(const void *name, const void *ref)
+{
+	return strcmp(name, (*(struct ref **)ref)->name);
+}
+
 static void receive_wanted_refs(struct packet_reader *reader,
 				struct ref **sought, int nr_sought)
 {
@@ -1305,20 +1334,16 @@
 	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
 		struct object_id oid;
 		const char *end;
-		int i;
+		struct ref **found;
 
 		if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ')
 			die(_("expected wanted-ref, got '%s'"), reader->line);
 
-		for (i = 0; i < nr_sought; i++) {
-			if (!strcmp(end, sought[i]->name)) {
-				oidcpy(&sought[i]->old_oid, &oid);
-				break;
-			}
-		}
-
-		if (i == nr_sought)
+		found = bsearch(end, sought, nr_sought, sizeof(*sought),
+				cmp_name_ref);
+		if (!found)
 			die(_("unexpected wanted-ref: '%s'"), reader->line);
+		oidcpy(&(*found)->old_oid, &oid);
 	}
 
 	if (reader->status != PACKET_READ_DELIM)
@@ -1337,6 +1362,8 @@
 				    int fd[2],
 				    const struct ref *orig_ref,
 				    struct ref **sought, int nr_sought,
+				    struct oid_array *shallows,
+				    struct shallow_info *si,
 				    char **pack_lockfile)
 {
 	struct ref *ref = copy_ref_list(orig_ref);
@@ -1411,7 +1438,7 @@
 		case FETCH_GET_PACK:
 			/* Check for shallow-info section */
 			if (process_section_header(&reader, "shallow-info", 1))
-				receive_shallow_info(args, &reader);
+				receive_shallow_info(args, &reader, shallows, si);
 
 			if (process_section_header(&reader, "wanted-refs", 1))
 				receive_wanted_refs(&reader, sought, nr_sought);
@@ -1615,9 +1642,8 @@
 }
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
-		       int fd[], struct child_process *conn,
+		       int fd[],
 		       const struct ref *ref,
-		       const char *dest,
 		       struct ref **sought, int nr_sought,
 		       struct oid_array *shallow,
 		       char **pack_lockfile,
@@ -1625,6 +1651,7 @@
 {
 	struct ref *ref_cpy;
 	struct shallow_info si;
+	struct oid_array shallows_scratch = OID_ARRAY_INIT;
 
 	fetch_pack_setup();
 	if (nr_sought)
@@ -1648,13 +1675,18 @@
 		packet_flush(fd[1]);
 		die(_("no matching remote head"));
 	}
-	prepare_shallow_info(&si, shallow);
-	if (version == protocol_v2)
+	if (version == protocol_v2) {
+		if (shallow->nr)
+			BUG("Protocol V2 does not provide shallows at this point in the fetch");
+		memset(&si, 0, sizeof(si));
 		ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
+					   &shallows_scratch, &si,
 					   pack_lockfile);
-	else
+	} else {
+		prepare_shallow_info(&si, shallow);
 		ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
 					&si, pack_lockfile);
+	}
 	reprepare_packed_git(the_repository);
 
 	if (!args->cloning && args->deepen) {
@@ -1676,6 +1708,7 @@
 	update_shallow(args, sought, nr_sought, &si);
 cleanup:
 	clear_shallow_info(&si);
+	oid_array_clear(&shallows_scratch);
 	return ref_cpy;
 }
 
diff --git a/fetch-pack.h b/fetch-pack.h
index 43ec344..67f6842 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -78,9 +78,8 @@
  * marked as such.
  */
 struct ref *fetch_pack(struct fetch_pack_args *args,
-		       int fd[], struct child_process *conn,
+		       int fd[],
 		       const struct ref *ref,
-		       const char *dest,
 		       struct ref **sought,
 		       int nr_sought,
 		       struct oid_array *shallow,
diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h
index b28d3a6..01e3aa8 100644
--- a/fmt-merge-msg.h
+++ b/fmt-merge-msg.h
@@ -2,6 +2,6 @@
 #define FMT_MERGE_MSG_H
 
 extern int merge_log_config;
-extern int fmt_merge_msg_config(const char *key, const char *value, void *cb);
+int fmt_merge_msg_config(const char *key, const char *value, void *cb);
 
 #endif /* FMT_MERGE_MSG_H */
diff --git a/fsck.c b/fsck.c
index 2260adb..4703f55 100644
--- a/fsck.c
+++ b/fsck.c
@@ -604,7 +604,7 @@
 	o_name = NULL;
 
 	while (desc.size) {
-		unsigned mode;
+		unsigned short mode;
 		const char *name;
 		const struct object_id *oid;
 
diff --git a/fsmonitor.h b/fsmonitor.h
index 01017c4..8489fa3 100644
--- a/fsmonitor.h
+++ b/fsmonitor.h
@@ -10,31 +10,31 @@
  * Read the fsmonitor index extension and (if configured) restore the
  * CE_FSMONITOR_VALID state.
  */
-extern int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
 
 /*
  * Fill the fsmonitor_dirty ewah bits with their state from the index,
  * before it is split during writing.
  */
-extern void fill_fsmonitor_bitmap(struct index_state *istate);
+void fill_fsmonitor_bitmap(struct index_state *istate);
 
 /*
  * Write the CE_FSMONITOR_VALID state into the fsmonitor index
  * extension.  Reads from the fsmonitor_dirty ewah in the index.
  */
-extern void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
 
 /*
  * Add/remove the fsmonitor index extension
  */
-extern void add_fsmonitor(struct index_state *istate);
-extern void remove_fsmonitor(struct index_state *istate);
+void add_fsmonitor(struct index_state *istate);
+void remove_fsmonitor(struct index_state *istate);
 
 /*
  * Add/remove the fsmonitor index extension as necessary based on the current
  * core.fsmonitor setting.
  */
-extern void tweak_fsmonitor(struct index_state *istate);
+void tweak_fsmonitor(struct index_state *istate);
 
 /*
  * Run the configured fsmonitor integration script and clear the
@@ -42,7 +42,7 @@
  * any corresponding untracked cache directory structures. Optimized to only
  * run the first time it is called.
  */
-extern void refresh_fsmonitor(struct index_state *istate);
+void refresh_fsmonitor(struct index_state *istate);
 
 /*
  * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 709d674..71158f7 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -6,7 +6,7 @@
 }
 
 command_list () {
-	grep -v '^#' "$1"
+	eval "grep -ve '^#' $exclude_programs" <"$1"
 }
 
 get_categories () {
@@ -93,6 +93,14 @@
 EOF
 }
 
+exclude_programs=
+while test "--exclude-program" = "$1"
+do
+	shift
+	exclude_programs="$exclude_programs -e \"^$1 \""
+	shift
+done
+
 echo "/* Automatically generated by generate-cmdlist.sh */
 struct cmdname_help {
 	const char *name;
diff --git a/gettext.h b/gettext.h
index 71255e5..bee52eb 100644
--- a/gettext.h
+++ b/gettext.h
@@ -28,11 +28,11 @@
 
 #define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
 
-extern int use_gettext_poison(void);
+int use_gettext_poison(void);
 
 #ifndef NO_GETTEXT
-extern void git_setup_gettext(void);
-extern int gettext_width(const char *s);
+void git_setup_gettext(void);
+int gettext_width(const char *s);
 #else
 static inline void git_setup_gettext(void)
 {
@@ -87,6 +87,6 @@
 #endif
 
 const char *get_preferred_languages(void);
-extern int is_utf8_locale(void);
+int is_utf8_locale(void);
 
 #endif
diff --git a/git-compat-util.h b/git-compat-util.h
index 31b4793..fd34601 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -200,6 +200,7 @@
 #include "compat/win32/path-utils.h"
 #include "compat/mingw.h"
 #elif defined(_MSC_VER)
+#include "compat/win32/path-utils.h"
 #include "compat/msvc.h"
 #else
 #include <sys/utsname.h>
@@ -249,7 +250,7 @@
 
 #ifdef MKDIR_WO_TRAILING_SLASH
 #define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
-extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
+int compat_mkdir_wo_trailing_slash(const char*, mode_t);
 #endif
 
 #ifdef NO_STRUCT_ITIMERVAL
@@ -267,9 +268,9 @@
 #include <libgen.h>
 #else
 #define basename gitbasename
-extern char *gitbasename(char *);
+char *gitbasename(char *);
 #define dirname gitdirname
-extern char *gitdirname(char *);
+char *gitdirname(char *);
 #endif
 
 #ifndef NO_ICONV
@@ -446,15 +447,15 @@
 struct strbuf;
 
 /* General helper functions */
-extern void vreportf(const char *prefix, const char *err, va_list params);
-extern NORETURN void usage(const char *err);
-extern NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
-extern void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void vreportf(const char *prefix, const char *err, va_list params);
+NORETURN void usage(const char *err);
+NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
+NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
+NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 #ifndef NO_OPENSSL
 #ifdef APPLE_COMMON_CRYPTO
@@ -482,15 +483,15 @@
 #define error_errno(...) (error_errno(__VA_ARGS__), const_error())
 #endif
 
-extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
-extern void set_error_routine(void (*routine)(const char *err, va_list params));
+void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
+void set_error_routine(void (*routine)(const char *err, va_list params));
 extern void (*get_error_routine(void))(const char *err, va_list params);
-extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
+void set_warn_routine(void (*routine)(const char *warn, va_list params));
 extern void (*get_warn_routine(void))(const char *warn, va_list params);
-extern void set_die_is_recursing_routine(int (*routine)(void));
+void set_die_is_recursing_routine(int (*routine)(void));
 
-extern int starts_with(const char *str, const char *prefix);
-extern int istarts_with(const char *str, const char *prefix);
+int starts_with(const char *str, const char *prefix);
+int istarts_with(const char *str, const char *prefix);
 
 /*
  * If the string "str" begins with the string found in "prefix", return 1.
@@ -613,8 +614,8 @@
 
 #define mmap git_mmap
 #define munmap git_munmap
-extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern int git_munmap(void *start, size_t length);
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int git_munmap(void *start, size_t length);
 
 #else /* NO_MMAP || USE_WIN32_MMAP */
 
@@ -668,17 +669,17 @@
 #undef stat
 #endif
 #define stat(path, buf) git_stat(path, buf)
-extern int git_stat(const char *, struct stat *);
+int git_stat(const char *, struct stat *);
 #ifdef fstat
 #undef fstat
 #endif
 #define fstat(fd, buf) git_fstat(fd, buf)
-extern int git_fstat(int, struct stat *);
+int git_fstat(int, struct stat *);
 #ifdef lstat
 #undef lstat
 #endif
 #define lstat(path, buf) git_lstat(path, buf)
-extern int git_lstat(const char *, struct stat *);
+int git_lstat(const char *, struct stat *);
 #endif
 
 #define DEFAULT_PACKED_GIT_LIMIT \
@@ -686,50 +687,50 @@
 
 #ifdef NO_PREAD
 #define pread git_pread
-extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
 #endif
 /*
  * Forward decl that will remind us if its twin in cache.h changes.
  * This function is used in compat/pread.c.  But we can't include
  * cache.h there.
  */
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
+ssize_t read_in_full(int fd, void *buf, size_t count);
 
 #ifdef NO_SETENV
 #define setenv gitsetenv
-extern int gitsetenv(const char *, const char *, int);
+int gitsetenv(const char *, const char *, int);
 #endif
 
 #ifdef NO_MKDTEMP
 #define mkdtemp gitmkdtemp
-extern char *gitmkdtemp(char *);
+char *gitmkdtemp(char *);
 #endif
 
 #ifdef NO_UNSETENV
 #define unsetenv gitunsetenv
-extern void gitunsetenv(const char *);
+void gitunsetenv(const char *);
 #endif
 
 #ifdef NO_STRCASESTR
 #define strcasestr gitstrcasestr
-extern char *gitstrcasestr(const char *haystack, const char *needle);
+char *gitstrcasestr(const char *haystack, const char *needle);
 #endif
 
 #ifdef NO_STRLCPY
 #define strlcpy gitstrlcpy
-extern size_t gitstrlcpy(char *, const char *, size_t);
+size_t gitstrlcpy(char *, const char *, size_t);
 #endif
 
 #ifdef NO_STRTOUMAX
 #define strtoumax gitstrtoumax
-extern uintmax_t gitstrtoumax(const char *, char **, int);
+uintmax_t gitstrtoumax(const char *, char **, int);
 #define strtoimax gitstrtoimax
-extern intmax_t gitstrtoimax(const char *, char **, int);
+intmax_t gitstrtoimax(const char *, char **, int);
 #endif
 
 #ifdef NO_HSTRERROR
 #define hstrerror githstrerror
-extern const char *githstrerror(int herror);
+const char *githstrerror(int herror);
 #endif
 
 #ifdef NO_MEMMEM
@@ -761,7 +762,7 @@
 #  endif
 #  define fopen(a,b) git_fopen(a,b)
 # endif
-extern FILE *git_fopen(const char*, const char*);
+FILE *git_fopen(const char*, const char*);
 #endif
 
 #ifdef SNPRINTF_RETURNS_BOGUS
@@ -769,14 +770,14 @@
 #undef snprintf
 #endif
 #define snprintf git_snprintf
-extern int git_snprintf(char *str, size_t maxsize,
-			const char *format, ...);
+int git_snprintf(char *str, size_t maxsize,
+		 const char *format, ...);
 #ifdef vsnprintf
 #undef vsnprintf
 #endif
 #define vsnprintf git_vsnprintf
-extern int git_vsnprintf(char *str, size_t maxsize,
-			 const char *format, va_list ap);
+int git_vsnprintf(char *str, size_t maxsize,
+		  const char *format, va_list ap);
 #endif
 
 #ifdef __GLIBC_PREREQ
@@ -805,11 +806,11 @@
 
 #ifdef NO_PTHREADS
 #define atexit git_atexit
-extern int git_atexit(void (*handler)(void));
+int git_atexit(void (*handler)(void));
 #endif
 
 typedef void (*try_to_free_t)(size_t);
-extern try_to_free_t set_try_to_free_routine(try_to_free_t);
+try_to_free_t set_try_to_free_routine(try_to_free_t);
 
 static inline size_t st_add(size_t a, size_t b)
 {
@@ -845,28 +846,28 @@
 # define xalloca(size)      (xmalloc(size))
 # define xalloca_free(p)    (free(p))
 #endif
-extern char *xstrdup(const char *str);
-extern void *xmalloc(size_t size);
-extern void *xmallocz(size_t size);
-extern void *xmallocz_gently(size_t size);
-extern void *xmemdupz(const void *data, size_t len);
-extern char *xstrndup(const char *str, size_t len);
-extern void *xrealloc(void *ptr, size_t size);
-extern void *xcalloc(size_t nmemb, size_t size);
-extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern int xopen(const char *path, int flags, ...);
-extern ssize_t xread(int fd, void *buf, size_t len);
-extern ssize_t xwrite(int fd, const void *buf, size_t len);
-extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
-extern int xdup(int fd);
-extern FILE *xfopen(const char *path, const char *mode);
-extern FILE *xfdopen(int fd, const char *mode);
-extern int xmkstemp(char *temp_filename);
-extern int xmkstemp_mode(char *temp_filename, int mode);
-extern char *xgetcwd(void);
-extern FILE *fopen_for_writing(const char *path);
-extern FILE *fopen_or_warn(const char *path, const char *mode);
+char *xstrdup(const char *str);
+void *xmalloc(size_t size);
+void *xmallocz(size_t size);
+void *xmallocz_gently(size_t size);
+void *xmemdupz(const void *data, size_t len);
+char *xstrndup(const char *str, size_t len);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int xopen(const char *path, int flags, ...);
+ssize_t xread(int fd, void *buf, size_t len);
+ssize_t xwrite(int fd, const void *buf, size_t len);
+ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
+int xdup(int fd);
+FILE *xfopen(const char *path, const char *mode);
+FILE *xfdopen(int fd, const char *mode);
+int xmkstemp(char *temp_filename);
+int xmkstemp_mode(char *temp_filename, int mode);
+char *xgetcwd(void);
+FILE *fopen_for_writing(const char *path);
+FILE *fopen_or_warn(const char *path, const char *mode);
 
 /*
  * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
@@ -966,13 +967,13 @@
 }
 
 __attribute__((format (printf, 3, 4)))
-extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
+int xsnprintf(char *dst, size_t max, const char *fmt, ...);
 
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
 
-extern int xgethostname(char *buf, size_t len);
+int xgethostname(char *buf, size_t len);
 
 /* in ctype.c, for kwset users */
 extern const unsigned char tolower_trans_tbl[256];
@@ -1257,7 +1258,7 @@
 	return (errno_ == ENOENT || errno_ == ENOTDIR);
 }
 
-extern int cmd_main(int, const char **);
+int cmd_main(int, const char **);
 
 /*
  * Intercept all calls to exit() and route them to trace2 to
@@ -1280,7 +1281,7 @@
  * an annotation, and does nothing in non-leak-checking builds.
  */
 #ifdef SUPPRESS_ANNOTATED_LEAKS
-extern void unleak_memory(const void *ptr, size_t len);
+void unleak_memory(const void *ptr, size_t len);
 #define UNLEAK(var) unleak_memory(&(var), sizeof(var))
 #else
 #define UNLEAK(var) do {} while (0)
diff --git a/git-send-email.perl b/git-send-email.perl
index 8200d58..24859a7 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1699,7 +1699,7 @@
 	# Now parse the message body
 	while(<$fh>) {
 		$message .=  $_;
-		if (/^([a-z-]*-by|Cc): (.*)/i) {
+		if (/^([a-z][a-z-]*-by|Cc): (.*)/i) {
 			chomp;
 			my ($what, $c) = ($1, $2);
 			# strip garbage for the address we'll use:
@@ -1872,7 +1872,7 @@
 	$message = MIME::Base64::decode($message)
 		if ($from eq 'base64');
 
-	$to = ($message =~ /.{999,}/) ? 'quoted-printable' : '8bit'
+	$to = ($message =~ /(?:.{999,}|\r)/) ? 'quoted-printable' : '8bit'
 		if $to eq 'auto';
 
 	die __("cannot send message as 7bit")
diff --git a/git-submodule.sh b/git-submodule.sh
index 2c0fb6d..c7f58c5 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -11,6 +11,7 @@
    or: $dashless [--quiet] init [--] [<path>...]
    or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
    or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
    or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
@@ -231,6 +232,13 @@
 		die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
 	fi
 
+	if test -d "$sm_path" &&
+		test -z $(git -C "$sm_path" rev-parse --show-cdup 2>/dev/null)
+	then
+	    git -C "$sm_path" rev-parse --verify -q HEAD >/dev/null ||
+	    die "$(eval_gettext "'\$sm_path' does not have a commit checked out")"
+	fi
+
 	if test -z "$force" &&
 		! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1
 	then
@@ -346,7 +354,7 @@
 		shift
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper foreach ${GIT_QUIET:+--quiet} ${recursive:+--recursive} -- "$@"
 }
 
 #
@@ -377,7 +385,7 @@
 		shift
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet}  "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet} -- "$@"
 }
 
 #
@@ -413,7 +421,7 @@
 		shift
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} -- "$@"
 }
 
 is_tip_reachable () (
@@ -542,6 +550,7 @@
 		${depth:+--depth "$depth"} \
 		$recommend_shallow \
 		$jobs \
+		-- \
 		"$@" || echo "#unmatched" $?
 	} | {
 	err=
@@ -686,6 +695,72 @@
 }
 
 #
+# Configures a submodule's default branch
+#
+# $@ = requested path
+#
+cmd_set_branch() {
+	unset_branch=false
+	branch=
+
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			# we don't do anything with this but we need to accept it
+			;;
+		-d|--default)
+			unset_branch=true
+			;;
+		-b|--branch)
+			case "$2" in '') usage ;; esac
+			branch=$2
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	if test $# -ne 1
+	then
+		usage
+	fi
+
+	# we can't use `git submodule--helper name` here because internally, it
+	# hashes the path so a trailing slash could lead to an unintentional no match
+	name="$(git submodule--helper list "$1" | cut -f2)"
+	if test -z "$name"
+	then
+		exit 1
+	fi
+
+	test -n "$branch"; has_branch=$?
+	test "$unset_branch" = true; has_unset_branch=$?
+
+	if test $((!$has_branch != !$has_unset_branch)) -eq 0
+	then
+		usage
+	fi
+
+	if test $has_branch -eq 0
+	then
+		git submodule--helper config submodule."$name".branch "$branch"
+	else
+		git submodule--helper config --unset submodule."$name".branch
+	fi
+}
+
+#
 # Show commit summary for submodules in index or working tree
 #
 # If '--cached' is given, show summary between index and given commit,
@@ -934,7 +1009,7 @@
 		shift
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} -- "$@"
 }
 #
 # Sync remote urls for submodules
@@ -967,7 +1042,7 @@
 		esac
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} -- "$@"
 }
 
 cmd_absorbgitdirs()
@@ -984,7 +1059,7 @@
 while test $# != 0 && test -z "$command"
 do
 	case "$1" in
-	add | foreach | init | deinit | update | status | summary | sync | absorbgitdirs)
+	add | foreach | init | deinit | update | set-branch | status | summary | sync | absorbgitdirs)
 		command=$1
 		;;
 	-q|--quiet)
@@ -1025,8 +1100,8 @@
     fi
 fi
 
-# "-b branch" is accepted only by "add"
-if test -n "$branch" && test "$command" != add
+# "-b branch" is accepted only by "add" and "set-branch"
+if test -n "$branch" && (test "$command" != add || test "$command" != set-branch)
 then
 	usage
 fi
@@ -1037,4 +1112,4 @@
 	usage
 fi
 
-"cmd_$command" "$@"
+"cmd_$(echo $command | sed -e s/-/_/g)" "$@"
diff --git a/git.c b/git.c
index 1c4fa3e..1bf9c94 100644
--- a/git.c
+++ b/git.c
@@ -498,7 +498,7 @@
 	{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
 	{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
 	{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
-	{ "difftool", cmd_difftool, RUN_SETUP | NEED_WORK_TREE },
+	{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
 	{ "fast-export", cmd_fast_export, RUN_SETUP },
 	{ "fetch", cmd_fetch, RUN_SETUP },
 	{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
@@ -570,7 +570,6 @@
 	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
 	{ "rm", cmd_rm, RUN_SETUP },
 	{ "send-pack", cmd_send_pack, RUN_SETUP },
-	{ "serve", cmd_serve, RUN_SETUP },
 	{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
 	{ "show", cmd_show, RUN_SETUP },
 	{ "show-branch", cmd_show_branch, RUN_SETUP },
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2594a4b..7fef19f 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -788,6 +788,38 @@
 # ======================================================================
 # input validation and dispatch
 
+# Various hash size-related values.
+my $sha1_len = 40;
+my $sha256_extra_len = 24;
+my $sha256_len = $sha1_len + $sha256_extra_len;
+
+# A regex matching $len hex characters. $len may be a range (e.g. 7,64).
+sub oid_nlen_regex {
+	my $len = shift;
+	my $hchr = qr/[0-9a-fA-F]/;
+	return qr/(?:(?:$hchr){$len})/;
+}
+
+# A regex matching two sets of $nlen hex characters, prefixed by the literal
+# string $prefix and with the literal string $infix between them.
+sub oid_nlen_prefix_infix_regex {
+	my $nlen = shift;
+	my $prefix = shift;
+	my $infix = shift;
+
+	my $rx = oid_nlen_regex($nlen);
+
+	return qr/^\Q$prefix\E$rx\Q$infix\E$rx$/;
+}
+
+# A regex matching a valid object ID.
+our $oid_regex;
+{
+	my $x = oid_nlen_regex($sha1_len);
+	my $y = oid_nlen_regex($sha256_extra_len);
+	$oid_regex = qr/(?:$x(?:$y)?)/;
+}
+
 # input parameters can be collected from a variety of sources (presently, CGI
 # and PATH_INFO), so we define an %input_params hash that collects them all
 # together during validation: this allows subsequent uses (e.g. href()) to be
@@ -1516,7 +1548,7 @@
 
 	return undef unless defined $input;
 	# textual hashes are O.K.
-	if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+	if ($input =~ m/^$oid_regex$/) {
 		return 1;
 	}
 	# it must be correct pathname
@@ -2028,6 +2060,9 @@
 sub format_log_line_html {
 	my $line = shift;
 
+	# Potentially abbreviated OID.
+	my $regex = oid_nlen_regex("7,64");
+
 	$line = esc_html($line, -nbsp=>1);
 	$line =~ s{
         \b
@@ -2037,10 +2072,10 @@
             (?<!-) # see strbuf_check_tag_ref(). Tags can't start with -
             [A-Za-z0-9.-]+
             (?!\.) # refs can't end with ".", see check_refname_format()
-            -g[0-9a-fA-F]{7,40}
+            -g$regex
             |
             # Just a normal looking Git SHA1
-            [0-9a-fA-F]{7,40}
+	    $regex
         )
         \b
     }{
@@ -2286,7 +2321,8 @@
 		         ')</span>';
 	}
 	# match <hash>
-	if ($line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
+	if ($line =~ oid_nlen_prefix_infix_regex($sha1_len, "index ", ",") |
+	    $line =~ oid_nlen_prefix_infix_regex($sha256_len, "index ", ",")) {
 		# can match only for combined diff
 		$line = 'index ';
 		for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
@@ -2308,7 +2344,8 @@
 			$line .= '0' x 7;
 		}
 
-	} elsif ($line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
+	} elsif ($line =~ oid_nlen_prefix_infix_regex($sha1_len, "index ", "..") |
+		 $line =~ oid_nlen_prefix_infix_regex($sha256_len, "index ", "..")) {
 		# can match only for ordinary diff
 		my ($from_link, $to_link);
 		if ($from->{'href'}) {
@@ -2834,7 +2871,7 @@
 	}
 
 	#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-	$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
+	$line =~ m/^([0-9]+) (.+) ($oid_regex)\t/;
 	if (defined $type && $type ne $2) {
 		# type doesn't match
 		return undef;
@@ -3333,7 +3370,7 @@
 
 	while (my $line = <$fd>) {
 		chomp $line;
-		if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) {
+		if ($line =~ m!^($oid_regex)\srefs/($type.*)$!) {
 			if (defined $refs{$1}) {
 				push @{$refs{$1}}, $2;
 			} else {
@@ -3407,7 +3444,7 @@
 	$tag{'id'} = $tag_id;
 	while (my $line = <$fd>) {
 		chomp $line;
-		if ($line =~ m/^object ([0-9a-fA-F]{40})$/) {
+		if ($line =~ m/^object ($oid_regex)$/) {
 			$tag{'object'} = $1;
 		} elsif ($line =~ m/^type (.+)$/) {
 			$tag{'type'} = $1;
@@ -3451,15 +3488,15 @@
 	}
 
 	my $header = shift @commit_lines;
-	if ($header !~ m/^[0-9a-fA-F]{40}/) {
+	if ($header !~ m/^$oid_regex/) {
 		return;
 	}
 	($co{'id'}, my @parents) = split ' ', $header;
 	while (my $line = shift @commit_lines) {
 		last if $line eq "\n";
-		if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
+		if ($line =~ m/^tree ($oid_regex)$/) {
 			$co{'tree'} = $1;
-		} elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
+		} elsif ((!defined $withparents) && ($line =~ m/^parent ($oid_regex)$/)) {
 			push @parents, $1;
 		} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
 			$co{'author'} = to_utf8($1);
@@ -3591,7 +3628,7 @@
 
 	# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M	ls-files.c'
 	# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M	rev-tree.c'
-	if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) {
+	if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ($oid_regex) ($oid_regex) (.)([0-9]{0,3})\t(.*)$/) {
 		$res{'from_mode'} = $1;
 		$res{'to_mode'} = $2;
 		$res{'from_id'} = $3;
@@ -3606,7 +3643,7 @@
 	}
 	# '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR	git-gui/git-gui.sh'
 	# combined diff (for merge commit)
-	elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) {
+	elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:$oid_regex )+)([a-zA-Z]+)\t(.*)$//) {
 		$res{'nparents'}  = length($1);
 		$res{'from_mode'} = [ split(' ', $2) ];
 		$res{'to_mode'} = pop @{$res{'from_mode'}};
@@ -3616,7 +3653,7 @@
 		$res{'to_file'} = unquote($5);
 	}
 	# 'c512b523472485aef4fff9e57b229d9d243c967f'
-	elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
+	elsif ($line =~ m/^($oid_regex)$/) {
 		$res{'commit'} = $1;
 	}
 
@@ -3644,7 +3681,7 @@
 
 	if ($opts{'-l'}) {
 		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa   16717	panic.c'
-		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
+		$line =~ m/^([0-9]+) (.+) ($oid_regex) +(-|[0-9]+)\t(.+)$/s;
 
 		$res{'mode'} = $1;
 		$res{'type'} = $2;
@@ -3657,7 +3694,7 @@
 		}
 	} else {
 		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-		$line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+		$line =~ m/^([0-9]+) (.+) ($oid_regex)\t(.+)$/s;
 
 		$res{'mode'} = $1;
 		$res{'type'} = $2;
@@ -4799,7 +4836,7 @@
 sub is_deleted {
 	my $diffinfo = shift;
 
-	return $diffinfo->{'to_id'} eq ('0' x 40);
+	return $diffinfo->{'to_id'} eq ('0' x 40) || $diffinfo->{'to_id'} eq ('0' x 64);
 }
 
 # does patch correspond to [previous] difftree raw line
@@ -6285,7 +6322,7 @@
 			              -class => "list subject"},
 			              chop_and_escape_str($co{'title'}, 50) . "<br/>");
 		} elsif (defined $set{'to_id'}) {
-			next if ($set{'to_id'} =~ m/^0{40}$/);
+			next if is_deleted(\%set);
 
 			print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
 			                             hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
@@ -6829,7 +6866,7 @@
 			# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
 			# no <lines in group> for subsequent lines in group of lines
 			my ($full_rev, $orig_lineno, $lineno, $group_size) =
-			   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
+			   ($line =~ /^($oid_regex) (\d+) (\d+)(?: (\d+))?$/);
 			if (!exists $metainfo{$full_rev}) {
 				$metainfo{$full_rev} = { 'nprevious' => 0 };
 			}
@@ -6879,7 +6916,7 @@
 			}
 			# 'previous' <sha1 of parent commit> <filename at commit>
 			if (exists $meta->{'previous'} &&
-			    $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+			    $meta->{'previous'} =~ /^($oid_regex) (.*)$/) {
 				$meta->{'parent'} = $1;
 				$meta->{'file_parent'} = unquote($2);
 			}
@@ -6996,7 +7033,7 @@
 		} else {
 			die_error(400, "No file name defined");
 		}
-	} elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+	} elsif ($hash =~ m/^$oid_regex$/) {
 		# blobs defined by non-textual hash id's can be cached
 		$expires = "+1d";
 	}
@@ -7057,7 +7094,7 @@
 		} else {
 			die_error(400, "No file name defined");
 		}
-	} elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+	} elsif ($hash =~ m/^$oid_regex$/) {
 		# blobs defined by non-textual hash id's can be cached
 		$expires = "+1d";
 	}
@@ -7515,7 +7552,7 @@
 
 	# non-textual hash id's can be cached
 	my $expires;
-	if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+	if ($hash =~ m/^$oid_regex$/) {
 		$expires = "+1d";
 	}
 	my $refs = git_get_references();
@@ -7609,7 +7646,7 @@
 		close $fd;
 
 		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
-		unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+		unless ($line && $line =~ m/^([0-9]+) (.+) ($oid_regex)\t/) {
 			die_error(404, "File or directory for given base does not exist");
 		}
 		$type = $2;
@@ -7649,7 +7686,7 @@
 				or die_error(404, "Blob diff not found");
 
 		} elsif (defined $hash &&
-		         $hash =~ /[0-9a-fA-F]{40}/) {
+		         $hash =~ $oid_regex) {
 			# try to find filename from $hash
 
 			# read filtered raw output
@@ -7659,7 +7696,7 @@
 			@difftree =
 				# ':100644 100644 03b21826... 3b93d5e7... M	ls-files.c'
 				# $hash == to_id
-				grep { /^:[0-7]{6} [0-7]{6} [0-9a-fA-F]{40} $hash/ }
+				grep { /^:[0-7]{6} [0-7]{6} $oid_regex $hash/ }
 				map { chomp; $_ } <$fd>;
 			close $fd
 				or die_error(404, "Reading git-diff-tree failed");
@@ -7682,8 +7719,8 @@
 		$hash        ||= $diffinfo{'to_id'};
 
 		# non-textual hash id's can be cached
-		if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ &&
-		    $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) {
+		if ($hash_base =~ m/^$oid_regex$/ &&
+		    $hash_parent_base =~ m/^$oid_regex$/) {
 			$expires = '+1d';
 		}
 
@@ -7819,7 +7856,7 @@
 		    $hash_parent ne '-c' && $hash_parent ne '--cc') {
 			# commitdiff with two commits given
 			my $hash_parent_short = $hash_parent;
-			if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+			if ($hash_parent =~ m/^$oid_regex$/) {
 				$hash_parent_short = substr($hash_parent, 0, 7);
 			}
 			$formats_nav .=
@@ -7928,7 +7965,7 @@
 
 	# non-textual hash id's can be cached
 	my $expires;
-	if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+	if ($hash =~ m/^$oid_regex$/) {
 		$expires = "+1d";
 	}
 
diff --git a/grep.h b/grep.h
index fb04893..1875880 100644
--- a/grep.h
+++ b/grep.h
@@ -186,17 +186,17 @@
 	void *output_priv;
 };
 
-extern void init_grep_defaults(struct repository *);
-extern int grep_config(const char *var, const char *value, void *);
-extern void grep_init(struct grep_opt *, struct repository *repo, const char *prefix);
+void init_grep_defaults(struct repository *);
+int grep_config(const char *var, const char *value, void *);
+void grep_init(struct grep_opt *, struct repository *repo, const char *prefix);
 void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
 
-extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
-extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
-extern void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
-extern void compile_grep_patterns(struct grep_opt *opt);
-extern void free_grep_patterns(struct grep_opt *opt);
-extern int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
+void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
+void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
+void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *);
+void compile_grep_patterns(struct grep_opt *opt);
+void free_grep_patterns(struct grep_opt *opt);
+int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size);
 
 struct grep_source {
 	char *name;
@@ -226,8 +226,8 @@
 
 int grep_source(struct grep_opt *opt, struct grep_source *gs);
 
-extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
-extern int grep_threads_ok(const struct grep_opt *opt);
+struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
+int grep_threads_ok(const struct grep_opt *opt);
 
 /*
  * Mutex used around access to the attributes machinery if
diff --git a/hash.h b/hash.h
index adde708..661c9f2 100644
--- a/hash.h
+++ b/hash.h
@@ -131,6 +131,8 @@
 int hash_algo_by_name(const char *name);
 /* Identical, except based on the format ID. */
 int hash_algo_by_id(uint32_t format_id);
+/* Identical, except based on the length. */
+int hash_algo_by_length(int len);
 /* Identical, except for a pointer to struct git_hash_algo. */
 static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
 {
diff --git a/hashmap.h b/hashmap.h
index d375d9c..f95593b 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -104,11 +104,11 @@
  * `memihash_cont` is a variant of `memihash` that allows a computation to be
  * continued with another chunk of data.
  */
-extern unsigned int strhash(const char *buf);
-extern unsigned int strihash(const char *buf);
-extern unsigned int memhash(const void *buf, size_t len);
-extern unsigned int memihash(const void *buf, size_t len);
-extern unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
+unsigned int strhash(const char *buf);
+unsigned int strihash(const char *buf);
+unsigned int memhash(const void *buf, size_t len);
+unsigned int memihash(const void *buf, size_t len);
+unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
 
 /*
  * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
@@ -216,7 +216,7 @@
  * parameter may be used to preallocate a sufficiently large table and thus
  * prevent expensive resizing. If 0, the table is dynamically resized.
  */
-extern void hashmap_init(struct hashmap *map,
+void hashmap_init(struct hashmap *map,
 			 hashmap_cmp_fn equals_function,
 			 const void *equals_function_data,
 			 size_t initial_size);
@@ -227,7 +227,7 @@
  * If `free_entries` is true, each hashmap_entry in the map is freed as well
  * using stdlibs free().
  */
-extern void hashmap_free(struct hashmap *map, int free_entries);
+void hashmap_free(struct hashmap *map, int free_entries);
 
 /* hashmap_entry functions */
 
@@ -284,7 +284,7 @@
  * If an entry with matching hash code is found, `key` and `keydata` are passed
  * to `hashmap_cmp_fn` to decide whether the entry matches the key.
  */
-extern void *hashmap_get(const struct hashmap *map, const void *key,
+void *hashmap_get(const struct hashmap *map, const void *key,
 			 const void *keydata);
 
 /*
@@ -316,7 +316,7 @@
  * `entry` is the hashmap_entry to start the search from, obtained via a previous
  * call to `hashmap_get` or `hashmap_get_next`.
  */
-extern void *hashmap_get_next(const struct hashmap *map, const void *entry);
+void *hashmap_get_next(const struct hashmap *map, const void *entry);
 
 /*
  * Adds a hashmap entry. This allows to add duplicate entries (i.e.
@@ -325,7 +325,7 @@
  * `map` is the hashmap structure.
  * `entry` is the entry to add.
  */
-extern void hashmap_add(struct hashmap *map, void *entry);
+void hashmap_add(struct hashmap *map, void *entry);
 
 /*
  * Adds or replaces a hashmap entry. If the hashmap contains duplicate
@@ -335,7 +335,7 @@
  * `entry` is the entry to add or replace.
  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
  */
-extern void *hashmap_put(struct hashmap *map, void *entry);
+void *hashmap_put(struct hashmap *map, void *entry);
 
 /*
  * Removes a hashmap entry matching the specified key. If the hashmap contains
@@ -344,7 +344,7 @@
  *
  * Argument explanation is the same as in `hashmap_get`.
  */
-extern void *hashmap_remove(struct hashmap *map, const void *key,
+void *hashmap_remove(struct hashmap *map, const void *key,
 		const void *keydata);
 
 /*
@@ -365,10 +365,10 @@
 };
 
 /* Initializes a `hashmap_iter` structure. */
-extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
+void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
 
 /* Returns the next hashmap_entry, or NULL if there are no more entries. */
-extern void *hashmap_iter_next(struct hashmap_iter *iter);
+void *hashmap_iter_next(struct hashmap_iter *iter);
 
 /* Initializes the iterator and returns the first entry, if any. */
 static inline void *hashmap_iter_first(struct hashmap *map,
@@ -429,7 +429,7 @@
  *
  * Uses a hashmap to store the pool of interned strings.
  */
-extern const void *memintern(const void *data, size_t len);
+const void *memintern(const void *data, size_t len);
 static inline const char *strintern(const char *string)
 {
 	return memintern(string, strlen(string));
diff --git a/help.h b/help.h
index a141e20..b8780fb 100644
--- a/help.h
+++ b/help.h
@@ -19,31 +19,31 @@
 		putchar(c);
 }
 
-extern void list_common_cmds_help(void);
-extern void list_all_cmds_help(void);
-extern void list_common_guides_help(void);
-extern void list_config_help(int for_human);
+void list_common_cmds_help(void);
+void list_all_cmds_help(void);
+void list_common_guides_help(void);
+void list_config_help(int for_human);
 
-extern void list_all_main_cmds(struct string_list *list);
-extern void list_all_other_cmds(struct string_list *list);
-extern void list_cmds_by_category(struct string_list *list,
-				  const char *category);
-extern void list_cmds_by_config(struct string_list *list);
-extern const char *help_unknown_cmd(const char *cmd);
-extern void load_command_list(const char *prefix,
-			      struct cmdnames *main_cmds,
-			      struct cmdnames *other_cmds);
-extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+void list_all_main_cmds(struct string_list *list);
+void list_all_other_cmds(struct string_list *list);
+void list_cmds_by_category(struct string_list *list,
+			   const char *category);
+void list_cmds_by_config(struct string_list *list);
+const char *help_unknown_cmd(const char *cmd);
+void load_command_list(const char *prefix,
+		       struct cmdnames *main_cmds,
+		       struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
-extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
-extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
-extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *cmds, const char *name);
+void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
 /*
  * call this to die(), when it is suspected that the user mistyped a
  * ref to the command, to give suggested "correct" refs.
  */
-extern void help_unknown_ref(const char *ref, const char *cmd, const char *error);
+void help_unknown_ref(const char *ref, const char *cmd, const char *error);
 
 static inline void list_config_item(struct string_list *list,
 				    const char *prefix,
diff --git a/http-backend.c b/http-backend.c
index 29e68e3..ec3144b 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -711,8 +711,11 @@
 	{"GET", "/objects/info/http-alternates$", get_text_file},
 	{"GET", "/objects/info/packs$", get_info_packs},
 	{"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{38}$", get_loose_object},
+	{"GET", "/objects/[0-9a-f]{2}/[0-9a-f]{62}$", get_loose_object},
 	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.pack$", get_pack_file},
+	{"GET", "/objects/pack/pack-[0-9a-f]{64}\\.pack$", get_pack_file},
 	{"GET", "/objects/pack/pack-[0-9a-f]{40}\\.idx$", get_idx_file},
+	{"GET", "/objects/pack/pack-[0-9a-f]{64}\\.idx$", get_idx_file},
 
 	{"POST", "/git-upload-pack$", service_rpc},
 	{"POST", "/git-receive-pack$", service_rpc}
diff --git a/http-push.c b/http-push.c
index b22c7ca..f675a96 100644
--- a/http-push.c
+++ b/http-push.c
@@ -145,7 +145,7 @@
 	char *url;
 	char *owner;
 	char *token;
-	char tmpfile_suffix[41];
+	char tmpfile_suffix[GIT_MAX_HEXSZ + 1];
 	time_t start_time;
 	long timeout;
 	int refreshing;
@@ -315,7 +315,8 @@
 		return;
 	}
 
-	fprintf(stderr,	"Fetching pack %s\n", sha1_to_hex(target->sha1));
+	fprintf(stderr,	"Fetching pack %s\n",
+		hash_to_hex(target->hash));
 	fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid));
 
 	preq = new_http_pack_request(target, repo->url);
@@ -398,7 +399,7 @@
 	request->dest = strbuf_detach(&buf, NULL);
 
 	append_remote_object_url(&buf, repo->url, hex, 0);
-	strbuf_add(&buf, request->lock->tmpfile_suffix, 41);
+	strbuf_add(&buf, request->lock->tmpfile_suffix, the_hash_algo->hexsz + 1);
 	request->url = strbuf_detach(&buf, NULL);
 
 	slot = get_active_slot();
@@ -757,8 +758,8 @@
 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 {
 	struct remote_lock *lock = (struct remote_lock *)ctx->userData;
-	git_SHA_CTX sha_ctx;
-	unsigned char lock_token_sha1[20];
+	git_hash_ctx hash_ctx;
+	unsigned char lock_token_hash[GIT_MAX_RAWSZ];
 
 	if (tag_closed && ctx->cdata) {
 		if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
@@ -770,12 +771,12 @@
 		} else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
 			lock->token = xstrdup(ctx->cdata);
 
-			git_SHA1_Init(&sha_ctx);
-			git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
-			git_SHA1_Final(lock_token_sha1, &sha_ctx);
+			the_hash_algo->init_fn(&hash_ctx);
+			the_hash_algo->update_fn(&hash_ctx, lock->token, strlen(lock->token));
+			the_hash_algo->final_fn(lock_token_hash, &hash_ctx);
 
 			lock->tmpfile_suffix[0] = '_';
-			memcpy(lock->tmpfile_suffix + 1, sha1_to_hex(lock_token_sha1), 40);
+			memcpy(lock->tmpfile_suffix + 1, hash_to_hex(lock_token_hash), the_hash_algo->hexsz);
 		}
 	}
 }
@@ -1017,7 +1018,7 @@
 /* extract hex from sharded "xx/x{38}" filename */
 static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
 {
-	if (strlen(path) != GIT_SHA1_HEXSZ + 1)
+	if (strlen(path) != the_hash_algo->hexsz + 1)
 		return -1;
 
 	if (hex_to_bytes(oid->hash, path, 1))
@@ -1025,7 +1026,7 @@
 	path += 2;
 	path++; /* skip '/' */
 
-	return hex_to_bytes(oid->hash + 1, path, GIT_SHA1_RAWSZ - 1);
+	return hex_to_bytes(oid->hash + 1, path, the_hash_algo->rawsz - 1);
 }
 
 static void process_ls_object(struct remote_ls_ctx *ls)
@@ -1373,7 +1374,7 @@
 	return count;
 }
 
-static int update_remote(unsigned char *sha1, struct remote_lock *lock)
+static int update_remote(const struct object_id *oid, struct remote_lock *lock)
 {
 	struct active_request_slot *slot;
 	struct slot_results results;
@@ -1382,7 +1383,7 @@
 
 	dav_headers = get_dav_token_headers(lock, DAV_HEADER_IF);
 
-	strbuf_addf(&out_buffer.buf, "%s\n", sha1_to_hex(sha1));
+	strbuf_addf(&out_buffer.buf, "%s\n", oid_to_hex(oid));
 
 	slot = get_active_slot();
 	slot->results = &results;
@@ -1947,7 +1948,7 @@
 		run_request_queue();
 
 		/* Update the remote branch if all went well */
-		if (aborted || !update_remote(ref->new_oid.hash, ref_lock))
+		if (aborted || !update_remote(&ref->new_oid, ref_lock))
 			rc = 1;
 
 		if (!rc)
diff --git a/http-walker.c b/http-walker.c
index 48b1b3a..fe15e32 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -442,9 +442,9 @@
 
 	if (walker->get_verbosely) {
 		fprintf(stderr, "Getting pack %s\n",
-			sha1_to_hex(target->sha1));
+			hash_to_hex(target->hash));
 		fprintf(stderr, " which contains %s\n",
-			sha1_to_hex(sha1));
+			hash_to_hex(sha1));
 	}
 
 	preq = new_http_pack_request(target, repo->base);
@@ -481,9 +481,9 @@
 	release_object_request(obj_req);
 }
 
-static int fetch_object(struct walker *walker, unsigned char *sha1)
+static int fetch_object(struct walker *walker, unsigned char *hash)
 {
-	char *hex = sha1_to_hex(sha1);
+	char *hex = hash_to_hex(hash);
 	int ret = 0;
 	struct object_request *obj_req = NULL;
 	struct http_object_request *req;
@@ -491,7 +491,7 @@
 
 	list_for_each(pos, head) {
 		obj_req = list_entry(pos, struct object_request, node);
-		if (hasheq(obj_req->oid.hash, sha1))
+		if (hasheq(obj_req->oid.hash, hash))
 			break;
 	}
 	if (obj_req == NULL)
@@ -556,20 +556,20 @@
 	return ret;
 }
 
-static int fetch(struct walker *walker, unsigned char *sha1)
+static int fetch(struct walker *walker, unsigned char *hash)
 {
 	struct walker_data *data = walker->data;
 	struct alt_base *altbase = data->alt;
 
-	if (!fetch_object(walker, sha1))
+	if (!fetch_object(walker, hash))
 		return 0;
 	while (altbase) {
-		if (!http_fetch_pack(walker, altbase, sha1))
+		if (!http_fetch_pack(walker, altbase, hash))
 			return 0;
 		fetch_alternates(walker, data->alt->base);
 		altbase = altbase->next;
 	}
-	return error("Unable to find %s under %s", sha1_to_hex(sha1),
+	return error("Unable to find %s under %s", hash_to_hex(hash),
 		     data->alt->base);
 }
 
diff --git a/http.c b/http.c
index 89fcd36..98fb06d 100644
--- a/http.c
+++ b/http.c
@@ -2071,7 +2071,7 @@
 	url = quote_ref_url(base, ref->name);
 	if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
 		strbuf_rtrim(&buffer);
-		if (buffer.len == 40)
+		if (buffer.len == the_hash_algo->hexsz)
 			ret = get_oid_hex(buffer.buf, &ref->old_oid);
 		else if (starts_with(buffer.buf, "ref: ")) {
 			ref->symref = xstrdup(buffer.buf + 5);
@@ -2085,19 +2085,19 @@
 }
 
 /* Helpers for fetching packs */
-static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
+static char *fetch_pack_index(unsigned char *hash, const char *base_url)
 {
 	char *url, *tmp;
 	struct strbuf buf = STRBUF_INIT;
 
 	if (http_is_verbose)
-		fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
+		fprintf(stderr, "Getting index for pack %s\n", hash_to_hex(hash));
 
 	end_url_with_slash(&buf, base_url);
-	strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
+	strbuf_addf(&buf, "objects/pack/pack-%s.idx", hash_to_hex(hash));
 	url = strbuf_detach(&buf, NULL);
 
-	strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
+	strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(hash));
 	tmp = strbuf_detach(&buf, NULL);
 
 	if (http_get_file(url, tmp, NULL) != HTTP_OK) {
@@ -2153,11 +2153,11 @@
 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
 {
 	struct http_get_options options = {0};
-	int ret = 0, i = 0;
-	char *url, *data;
+	int ret = 0;
+	char *url;
+	const char *data;
 	struct strbuf buf = STRBUF_INIT;
-	unsigned char hash[GIT_MAX_RAWSZ];
-	const unsigned hexsz = the_hash_algo->hexsz;
+	struct object_id oid;
 
 	end_url_with_slash(&buf, base_url);
 	strbuf_addstr(&buf, "objects/info/packs");
@@ -2169,24 +2169,17 @@
 		goto cleanup;
 
 	data = buf.buf;
-	while (i < buf.len) {
-		switch (data[i]) {
-		case 'P':
-			i++;
-			if (i + hexsz + 12 <= buf.len &&
-			    starts_with(data + i, " pack-") &&
-			    starts_with(data + i + hexsz + 6, ".pack\n")) {
-				get_sha1_hex(data + i + 6, hash);
-				fetch_and_setup_pack_index(packs_head, hash,
-						      base_url);
-				i += hexsz + 11;
-				break;
-			}
-		default:
-			while (i < buf.len && data[i] != '\n')
-				i++;
+	while (*data) {
+		if (skip_prefix(data, "P pack-", &data) &&
+		    !parse_oid_hex(data, &oid, &data) &&
+		    skip_prefix(data, ".pack", &data) &&
+		    (*data == '\n' || *data == '\0')) {
+			fetch_and_setup_pack_index(packs_head, oid.hash, base_url);
+		} else {
+			data = strchrnul(data, '\n');
 		}
-		i++;
+		if (*data)
+			data++; /* skip past newline */
 	}
 
 cleanup:
@@ -2242,10 +2235,10 @@
 		return -1;
 	}
 
-	unlink(sha1_pack_index_name(p->sha1));
+	unlink(sha1_pack_index_name(p->hash));
 
-	if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->sha1))
-	 || finalize_object_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
+	if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->hash))
+	 || finalize_object_file(tmp_idx, sha1_pack_index_name(p->hash))) {
 		free(tmp_idx);
 		return -1;
 	}
@@ -2268,10 +2261,10 @@
 
 	end_url_with_slash(&buf, base_url);
 	strbuf_addf(&buf, "objects/pack/pack-%s.pack",
-		sha1_to_hex(target->sha1));
+		hash_to_hex(target->hash));
 	preq->url = strbuf_detach(&buf, NULL);
 
-	strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->sha1));
+	strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->hash));
 	preq->packfile = fopen(preq->tmpfile.buf, "a");
 	if (!preq->packfile) {
 		error("Unable to open local file %s for pack",
@@ -2295,7 +2288,8 @@
 		if (http_is_verbose)
 			fprintf(stderr,
 				"Resuming fetch of pack %s at byte %"PRIuMAX"\n",
-				sha1_to_hex(target->sha1), (uintmax_t)prev_posn);
+				hash_to_hex(target->hash),
+				(uintmax_t)prev_posn);
 		http_opt_request_remainder(preq->slot->curl, prev_posn);
 	}
 
@@ -2342,8 +2336,8 @@
 		freq->stream.next_out = expn;
 		freq->stream.avail_out = sizeof(expn);
 		freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
-		git_SHA1_Update(&freq->c, expn,
-				sizeof(expn) - freq->stream.avail_out);
+		the_hash_algo->update_fn(&freq->c, expn,
+					 sizeof(expn) - freq->stream.avail_out);
 	} while (freq->stream.avail_in && freq->zret == Z_OK);
 	return size;
 }
@@ -2401,7 +2395,7 @@
 
 	git_inflate_init(&freq->stream);
 
-	git_SHA1_Init(&freq->c);
+	the_hash_algo->init_fn(&freq->c);
 
 	freq->url = get_remote_object_url(base_url, hex, 0);
 
@@ -2436,7 +2430,7 @@
 	if (prev_read == -1) {
 		memset(&freq->stream, 0, sizeof(freq->stream));
 		git_inflate_init(&freq->stream);
-		git_SHA1_Init(&freq->c);
+		the_hash_algo->init_fn(&freq->c);
 		if (prev_posn>0) {
 			prev_posn = 0;
 			lseek(freq->localfile, 0, SEEK_SET);
@@ -2507,7 +2501,7 @@
 	}
 
 	git_inflate_end(&freq->stream);
-	git_SHA1_Final(freq->real_oid.hash, &freq->c);
+	the_hash_algo->final_fn(freq->real_oid.hash, &freq->c);
 	if (freq->zret != Z_STREAM_END) {
 		unlink_or_warn(freq->tmpfile.buf);
 		return -1;
diff --git a/http.h b/http.h
index f0d271b..b429f1c 100644
--- a/http.h
+++ b/http.h
@@ -75,18 +75,18 @@
 };
 
 /* Curl request read/write callbacks */
-extern size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
-extern size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
+size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
 #ifndef NO_CURL_IOCTL
-extern curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
+curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
 #endif
 
 /* Slot lifecycle functions */
-extern struct active_request_slot *get_active_slot(void);
-extern int start_active_slot(struct active_request_slot *slot);
-extern void run_active_slot(struct active_request_slot *slot);
-extern void finish_all_active_slots(void);
+struct active_request_slot *get_active_slot(void);
+int start_active_slot(struct active_request_slot *slot);
+void run_active_slot(struct active_request_slot *slot);
+void finish_all_active_slots(void);
 
 /*
  * This will run one slot to completion in a blocking manner, similar to how
@@ -98,15 +98,15 @@
 		 struct slot_results *results);
 
 #ifdef USE_CURL_MULTI
-extern void fill_active_slots(void);
-extern void add_fill_function(void *data, int (*fill)(void *));
-extern void step_active_slots(void);
+void fill_active_slots(void);
+void add_fill_function(void *data, int (*fill)(void *));
+void step_active_slots(void);
 #endif
 
-extern void http_init(struct remote *remote, const char *url,
-		      int proactive_auth);
-extern void http_cleanup(void);
-extern struct curl_slist *http_copy_default_headers(void);
+void http_init(struct remote *remote, const char *url,
+	       int proactive_auth);
+void http_cleanup(void);
+struct curl_slist *http_copy_default_headers(void);
 
 extern long int git_curl_ipresolve;
 extern int active_requests;
@@ -146,11 +146,11 @@
 			   size_t errorlen);
 
 /* Helpers for modifying and creating URLs */
-extern void append_remote_object_url(struct strbuf *buf, const char *url,
-				     const char *hex,
-				     int only_two_digit_prefix);
-extern char *get_remote_object_url(const char *url, const char *hex,
-				   int only_two_digit_prefix);
+void append_remote_object_url(struct strbuf *buf, const char *url,
+			      const char *hex,
+			      int only_two_digit_prefix);
+char *get_remote_object_url(const char *url, const char *hex,
+			    int only_two_digit_prefix);
 
 /* Options for http_get_*() */
 struct http_get_options {
@@ -204,11 +204,11 @@
  */
 int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
 
-extern int http_fetch_ref(const char *base, struct ref *ref);
+int http_fetch_ref(const char *base, struct ref *ref);
 
 /* Helpers for fetching packs */
-extern int http_get_info_packs(const char *base_url,
-	struct packed_git **packs_head);
+int http_get_info_packs(const char *base_url,
+			struct packed_git **packs_head);
 
 struct http_pack_request {
 	char *url;
@@ -219,10 +219,10 @@
 	struct active_request_slot *slot;
 };
 
-extern struct http_pack_request *new_http_pack_request(
+struct http_pack_request *new_http_pack_request(
 	struct packed_git *target, const char *base_url);
-extern int finish_http_pack_request(struct http_pack_request *preq);
-extern void release_http_pack_request(struct http_pack_request *preq);
+int finish_http_pack_request(struct http_pack_request *preq);
+void release_http_pack_request(struct http_pack_request *preq);
 
 /* Helpers for fetching object */
 struct http_object_request {
@@ -234,19 +234,19 @@
 	long http_code;
 	struct object_id oid;
 	struct object_id real_oid;
-	git_SHA_CTX c;
+	git_hash_ctx c;
 	git_zstream stream;
 	int zret;
 	int rename;
 	struct active_request_slot *slot;
 };
 
-extern struct http_object_request *new_http_object_request(
+struct http_object_request *new_http_object_request(
 	const char *base_url, const struct object_id *oid);
-extern void process_http_object_request(struct http_object_request *freq);
-extern int finish_http_object_request(struct http_object_request *freq);
-extern void abort_http_object_request(struct http_object_request *freq);
-extern void release_http_object_request(struct http_object_request *freq);
+void process_http_object_request(struct http_object_request *freq);
+int finish_http_object_request(struct http_object_request *freq);
+void abort_http_object_request(struct http_object_request *freq);
+void release_http_object_request(struct http_object_request *freq);
 
 /* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */
 void setup_curl_trace(CURL *handle);
diff --git a/khash.h b/khash.h
index 532109c..af747a6 100644
--- a/khash.h
+++ b/khash.h
@@ -69,14 +69,14 @@
 		khval_t *vals; \
 	} kh_##name##_t;
 
-#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)	 					\
-	extern kh_##name##_t *kh_init_##name(void);							\
-	extern void kh_destroy_##name(kh_##name##_t *h);					\
-	extern void kh_clear_##name(kh_##name##_t *h);						\
-	extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); 	\
-	extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
-	extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
-	extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)	 			\
+	kh_##name##_t *kh_init_##name(void);						\
+	void kh_destroy_##name(kh_##name##_t *h);					\
+	void kh_clear_##name(kh_##name##_t *h);						\
+	khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+	int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+	khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+	void kh_del_##name(kh_##name##_t *h, khint_t x);
 
 #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
 	SCOPE kh_##name##_t *kh_init_##name(void) {							\
@@ -332,4 +332,22 @@
 KHASH_INIT(sha1_pos, const unsigned char *, int, 1, sha1hash, __kh_oid_cmp)
 typedef kh_sha1_pos_t khash_sha1_pos;
 
+static inline unsigned int oid_hash(struct object_id oid)
+{
+	return sha1hash(oid.hash);
+}
+
+static inline int oid_equal(struct object_id a, struct object_id b)
+{
+	return oideq(&a, &b);
+}
+
+KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
+
+KHASH_INIT(oid_map, struct object_id, void *, 1, oid_hash, oid_equal)
+typedef kh_oid_t khash_oid_map;
+
+KHASH_INIT(oid_pos, struct object_id, int, 1, oid_hash, oid_equal)
+typedef kh_oid_pos_t khash_oid_pos;
+
 #endif /* __AC_KHASH_H */
diff --git a/kwset.h b/kwset.h
index 583f626..df99a92 100644
--- a/kwset.h
+++ b/kwset.h
@@ -37,16 +37,16 @@
    if enough memory cannot be obtained.  The argument if non-NULL
    specifies a table of character translations to be applied to all
    pattern and search text. */
-extern kwset_t kwsalloc(unsigned char const *);
+kwset_t kwsalloc(unsigned char const *);
 
 /* Incrementally extend the keyword set to include the given string.
    Return NULL for success, or an error message.  Remember an index
    number for each keyword included in the set. */
-extern const char *kwsincr(kwset_t, char const *, size_t);
+const char *kwsincr(kwset_t, char const *, size_t);
 
 /* When the keyword set has been completely built, prepare it for
    use.  Return NULL for success, or an error message. */
-extern const char *kwsprep(kwset_t);
+const char *kwsprep(kwset_t);
 
 /* Search through the given buffer for a member of the keyword set.
    Return a pointer to the leftmost longest match found, or NULL if
@@ -54,8 +54,8 @@
    the matching substring in the integer it points to.  Similarly,
    if foundindex is non-NULL, store the index of the particular
    keyword found therein. */
-extern size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
+size_t kwsexec(kwset_t, char const *, size_t, struct kwsmatch *);
 
 /* Deallocate the given keyword set and all its associated storage. */
-extern void kwsfree(kwset_t);
+void kwsfree(kwset_t);
 
diff --git a/line-log.c b/line-log.c
index 59248e3..0a17b21 100644
--- a/line-log.c
+++ b/line-log.c
@@ -498,7 +498,7 @@
 
 static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
 {
-	unsigned mode;
+	unsigned short mode;
 	struct object_id oid;
 
 	if (get_tree_entry(&commit->object.oid, spec->path, &oid, &mode))
diff --git a/line-log.h b/line-log.h
index e2a5ee7..8ee7a2b 100644
--- a/line-log.h
+++ b/line-log.h
@@ -25,17 +25,17 @@
 	struct range_set target;
 };
 
-extern void range_set_init(struct range_set *, size_t prealloc);
-extern void range_set_release(struct range_set *);
+void range_set_init(struct range_set *, size_t prealloc);
+void range_set_release(struct range_set *);
 /* Range includes start; excludes end */
-extern void range_set_append_unsafe(struct range_set *, long start, long end);
+void range_set_append_unsafe(struct range_set *, long start, long end);
 /* New range must begin at or after end of last added range */
-extern void range_set_append(struct range_set *, long start, long end);
+void range_set_append(struct range_set *, long start, long end);
 /*
  * In-place pass of sorting and merging the ranges in the range set,
  * to sort and make the ranges disjoint.
  */
-extern void sort_and_merge_range_set(struct range_set *);
+void sort_and_merge_range_set(struct range_set *);
 
 /* Linked list of interesting files and their associated ranges.  The
  * list must be kept sorted by path.
@@ -54,10 +54,10 @@
 	struct diff_ranges diff;
 };
 
-extern void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
+void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
 
-extern int line_log_filter(struct rev_info *rev);
+int line_log_filter(struct rev_info *rev);
 
-extern int line_log_print(struct rev_info *rev, struct commit *commit);
+int line_log_print(struct rev_info *rev, struct commit *commit);
 
 #endif /* LINE_LOG_H */
diff --git a/list-objects.c b/list-objects.c
index dc77361..b5651dd 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -125,6 +125,11 @@
 
 		if (S_ISDIR(entry.mode)) {
 			struct tree *t = lookup_tree(ctx->revs->repo, &entry.oid);
+			if (!t) {
+				die(_("entry '%s' in tree %s has tree mode, "
+				      "but is not a tree"),
+				    entry.path, oid_to_hex(&tree->object.oid));
+			}
 			t->object.flags |= NOT_USER_GIVEN;
 			process_tree(ctx, t, base, entry.path);
 		}
@@ -133,6 +138,11 @@
 					base, entry.path);
 		else {
 			struct blob *b = lookup_blob(ctx->revs->repo, &entry.oid);
+			if (!b) {
+				die(_("entry '%s' in tree %s has blob mode, "
+				      "but is not a blob"),
+				    entry.path, oid_to_hex(&tree->object.oid));
+			}
 			b->object.flags |= NOT_USER_GIVEN;
 			process_blob(ctx, b, base, entry.path);
 		}
@@ -364,6 +374,9 @@
 			struct tree *tree = get_commit_tree(commit);
 			tree->object.flags |= NOT_USER_GIVEN;
 			add_pending_tree(ctx->revs, tree);
+		} else if (commit->object.parsed) {
+			die(_("unable to load root tree for commit %s"),
+			      oid_to_hex(&commit->object.oid));
 		}
 		ctx->show_commit(commit, ctx->show_data);
 
diff --git a/lockfile.h b/lockfile.h
index 35403cc..9843053 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -159,7 +159,7 @@
  * timeout_ms is -1, retry indefinitely. The flags argument and error
  * handling are described above.
  */
-extern int hold_lock_file_for_update_timeout(
+int hold_lock_file_for_update_timeout(
 		struct lock_file *lk, const char *path,
 		int flags, long timeout_ms);
 
@@ -188,8 +188,8 @@
  * of `hold_lock_file_for_update()` to lock `path`. `err` should be the
  * `errno` set by the failing call.
  */
-extern void unable_to_lock_message(const char *path, int err,
-				   struct strbuf *buf);
+void unable_to_lock_message(const char *path, int err,
+			    struct strbuf *buf);
 
 /*
  * Emit an appropriate error message and `die()` following the failure
@@ -197,7 +197,7 @@
  * `errno` set by the failing
  * call.
  */
-extern NORETURN void unable_to_lock_die(const char *path, int err);
+NORETURN void unable_to_lock_die(const char *path, int err);
 
 /*
  * Associate a stdio stream with the lockfile (which must still be
@@ -234,7 +234,7 @@
  * Return the path of the file that is locked by the specified
  * lock_file object. The caller must free the memory.
  */
-extern char *get_locked_file_path(struct lock_file *lk);
+char *get_locked_file_path(struct lock_file *lk);
 
 /*
  * If the lockfile is still open, close it (and the file pointer if it
@@ -282,7 +282,7 @@
  * call `commit_lock_file()` for a `lock_file` object that is not
  * currently locked.
  */
-extern int commit_lock_file(struct lock_file *lk);
+int commit_lock_file(struct lock_file *lk);
 
 /*
  * Like `commit_lock_file()`, but rename the lockfile to the provided
diff --git a/ls-refs.h b/ls-refs.h
index b62877e..7e5646f 100644
--- a/ls-refs.h
+++ b/ls-refs.h
@@ -4,7 +4,7 @@
 struct repository;
 struct argv_array;
 struct packet_reader;
-extern int ls_refs(struct repository *r, struct argv_array *keys,
-		   struct packet_reader *request);
+int ls_refs(struct repository *r, struct argv_array *keys,
+	    struct packet_reader *request);
 
 #endif /* LS_REFS_H */
diff --git a/mailinfo.h b/mailinfo.h
index 6830e1e..79b1d67 100644
--- a/mailinfo.h
+++ b/mailinfo.h
@@ -39,8 +39,8 @@
 	int input_error;
 };
 
-extern void setup_mailinfo(struct mailinfo *);
-extern int mailinfo(struct mailinfo *, const char *msg, const char *patch);
-extern void clear_mailinfo(struct mailinfo *);
+void setup_mailinfo(struct mailinfo *);
+int mailinfo(struct mailinfo *, const char *msg, const char *patch);
+void clear_mailinfo(struct mailinfo *);
 
 #endif /* MAILINFO_H */
diff --git a/match-trees.c b/match-trees.c
index ddc4d39..9d1ec8d 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -140,7 +140,7 @@
 	while (one.size) {
 		const char *path;
 		const struct object_id *elem;
-		unsigned mode;
+		unsigned short mode;
 		int score;
 
 		elem = tree_entry_extract(&one, &path, &mode);
@@ -196,7 +196,7 @@
 	rewrite_here = NULL;
 	while (desc.size) {
 		const char *name;
-		unsigned mode;
+		unsigned short mode;
 
 		tree_entry_extract(&desc, &name, &mode);
 		if (strlen(name) == toplen &&
@@ -285,7 +285,7 @@
 
 	if (add_score < del_score) {
 		/* We need to pick a subtree of two */
-		unsigned mode;
+		unsigned short mode;
 
 		if (!*del_prefix)
 			return;
@@ -313,7 +313,7 @@
 		   const char *shift_prefix)
 {
 	struct object_id sub1, sub2;
-	unsigned mode1, mode2;
+	unsigned short mode1, mode2;
 	unsigned candidate = 0;
 
 	/* Can hash2 be a tree at shift_prefix in tree hash1? */
diff --git a/merge-blobs.h b/merge-blobs.h
index cc31038..13cf966 100644
--- a/merge-blobs.h
+++ b/merge-blobs.h
@@ -4,8 +4,8 @@
 struct blob;
 struct index_state;
 
-extern void *merge_blobs(struct index_state *, const char *,
-			 struct blob *, struct blob *,
-			 struct blob *, unsigned long *);
+void *merge_blobs(struct index_state *, const char *,
+		  struct blob *, struct blob *,
+		  struct blob *, unsigned long *);
 
 #endif /* MERGE_BLOBS_H */
diff --git a/merge-recursive.c b/merge-recursive.c
index 6c40c61..a7bcfcb 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -115,32 +115,32 @@
 	hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);
 }
 
-static void flush_output(struct merge_options *o)
+static void flush_output(struct merge_options *opt)
 {
-	if (o->buffer_output < 2 && o->obuf.len) {
-		fputs(o->obuf.buf, stdout);
-		strbuf_reset(&o->obuf);
+	if (opt->buffer_output < 2 && opt->obuf.len) {
+		fputs(opt->obuf.buf, stdout);
+		strbuf_reset(&opt->obuf);
 	}
 }
 
-static int err(struct merge_options *o, const char *err, ...)
+static int err(struct merge_options *opt, const char *err, ...)
 {
 	va_list params;
 
-	if (o->buffer_output < 2)
-		flush_output(o);
+	if (opt->buffer_output < 2)
+		flush_output(opt);
 	else {
-		strbuf_complete(&o->obuf, '\n');
-		strbuf_addstr(&o->obuf, "error: ");
+		strbuf_complete(&opt->obuf, '\n');
+		strbuf_addstr(&opt->obuf, "error: ");
 	}
 	va_start(params, err);
-	strbuf_vaddf(&o->obuf, err, params);
+	strbuf_vaddf(&opt->obuf, err, params);
 	va_end(params);
-	if (o->buffer_output > 1)
-		strbuf_addch(&o->obuf, '\n');
+	if (opt->buffer_output > 1)
+		strbuf_addch(&opt->obuf, '\n');
 	else {
-		error("%s", o->obuf.buf);
-		strbuf_reset(&o->obuf);
+		error("%s", opt->obuf.buf);
+		strbuf_reset(&opt->obuf);
 	}
 
 	return -1;
@@ -163,6 +163,11 @@
 	return lookup_tree(repo, &shifted);
 }
 
+static inline void set_commit_tree(struct commit *c, struct tree *t)
+{
+	c->maybe_tree = t;
+}
+
 static struct commit *make_virtual_commit(struct repository *repo,
 					  struct tree *tree,
 					  const char *comment)
@@ -170,7 +175,7 @@
 	struct commit *commit = alloc_commit_node(repo);
 
 	set_merge_remote_desc(commit, comment, (struct object *)commit);
-	commit->maybe_tree = tree;
+	set_commit_tree(commit, tree);
 	commit->object.parsed = 1;
 	return commit;
 }
@@ -196,163 +201,148 @@
 	RENAME_TWO_FILES_TO_ONE
 };
 
-struct rename_conflict_info {
-	enum rename_type rename_type;
-	struct diff_filepair *pair1;
-	struct diff_filepair *pair2;
-	const char *branch1;
-	const char *branch2;
-	struct stage_data *dst_entry1;
-	struct stage_data *dst_entry2;
-	struct diff_filespec ren1_other;
-	struct diff_filespec ren2_other;
-};
-
 /*
  * Since we want to write the index eventually, we cannot reuse the index
  * for these (temporary) data.
  */
 struct stage_data {
-	struct {
-		unsigned mode;
-		struct object_id oid;
-	} stages[4];
+	struct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */
 	struct rename_conflict_info *rename_conflict_info;
 	unsigned processed:1;
 };
 
+struct rename {
+	unsigned processed:1;
+	struct diff_filepair *pair;
+	const char *branch; /* branch that the rename occurred on */
+	/*
+	 * If directory rename detection affected this rename, what was its
+	 * original type ('A' or 'R') and it's original destination before
+	 * the directory rename (otherwise, '\0' and NULL for these two vars).
+	 */
+	char dir_rename_original_type;
+	char *dir_rename_original_dest;
+	/*
+	 * Purpose of src_entry and dst_entry:
+	 *
+	 * If 'before' is renamed to 'after' then src_entry will contain
+	 * the versions of 'before' from the merge_base, HEAD, and MERGE in
+	 * stages 1, 2, and 3; dst_entry will contain the respective
+	 * versions of 'after' in corresponding locations.  Thus, we have a
+	 * total of six modes and oids, though some will be null.  (Stage 0
+	 * is ignored; we're interested in handling conflicts.)
+	 *
+	 * Since we don't turn on break-rewrites by default, neither
+	 * src_entry nor dst_entry can have all three of their stages have
+	 * non-null oids, meaning at most four of the six will be non-null.
+	 * Also, since this is a rename, both src_entry and dst_entry will
+	 * have at least one non-null oid, meaning at least two will be
+	 * non-null.  Of the six oids, a typical rename will have three be
+	 * non-null.  Only two implies a rename/delete, and four implies a
+	 * rename/add.
+	 */
+	struct stage_data *src_entry;
+	struct stage_data *dst_entry;
+};
+
+struct rename_conflict_info {
+	enum rename_type rename_type;
+	struct rename *ren1;
+	struct rename *ren2;
+};
+
 static inline void setup_rename_conflict_info(enum rename_type rename_type,
-					      struct diff_filepair *pair1,
-					      struct diff_filepair *pair2,
-					      const char *branch1,
-					      const char *branch2,
-					      struct stage_data *dst_entry1,
-					      struct stage_data *dst_entry2,
-					      struct merge_options *o,
-					      struct stage_data *src_entry1,
-					      struct stage_data *src_entry2)
+					      struct merge_options *opt,
+					      struct rename *ren1,
+					      struct rename *ren2)
 {
-	int ostage1 = 0, ostage2;
 	struct rename_conflict_info *ci;
 
 	/*
 	 * When we have two renames involved, it's easiest to get the
 	 * correct things into stage 2 and 3, and to make sure that the
 	 * content merge puts HEAD before the other branch if we just
-	 * ensure that branch1 == o->branch1.  So, simply flip arguments
+	 * ensure that branch1 == opt->branch1.  So, simply flip arguments
 	 * around if we don't have that.
 	 */
-	if (dst_entry2 && branch1 != o->branch1) {
-		setup_rename_conflict_info(rename_type,
-					   pair2,      pair1,
-					   branch2,    branch1,
-					   dst_entry2, dst_entry1,
-					   o,
-					   src_entry2, src_entry1);
+	if (ren2 && ren1->branch != opt->branch1) {
+		setup_rename_conflict_info(rename_type, opt, ren2, ren1);
 		return;
 	}
 
 	ci = xcalloc(1, sizeof(struct rename_conflict_info));
 	ci->rename_type = rename_type;
-	ci->pair1 = pair1;
-	ci->branch1 = branch1;
-	ci->branch2 = branch2;
+	ci->ren1 = ren1;
+	ci->ren2 = ren2;
 
-	ci->dst_entry1 = dst_entry1;
-	dst_entry1->rename_conflict_info = ci;
-	dst_entry1->processed = 0;
-
-	assert(!pair2 == !dst_entry2);
-	if (dst_entry2) {
-		ci->dst_entry2 = dst_entry2;
-		ci->pair2 = pair2;
-		dst_entry2->rename_conflict_info = ci;
-	}
-
-	/*
-	 * For each rename, there could have been
-	 * modifications on the side of history where that
-	 * file was not renamed.
-	 */
-	if (rename_type == RENAME_ADD ||
-	    rename_type == RENAME_TWO_FILES_TO_ONE) {
-		ostage1 = o->branch1 == branch1 ? 3 : 2;
-
-		ci->ren1_other.path = pair1->one->path;
-		oidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);
-		ci->ren1_other.mode = src_entry1->stages[ostage1].mode;
-	}
-
-	if (rename_type == RENAME_TWO_FILES_TO_ONE) {
-		ostage2 = ostage1 ^ 1;
-
-		ci->ren2_other.path = pair2->one->path;
-		oidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);
-		ci->ren2_other.mode = src_entry2->stages[ostage2].mode;
+	ci->ren1->dst_entry->processed = 0;
+	ci->ren1->dst_entry->rename_conflict_info = ci;
+	if (ren2) {
+		ci->ren2->dst_entry->rename_conflict_info = ci;
 	}
 }
 
-static int show(struct merge_options *o, int v)
+static int show(struct merge_options *opt, int v)
 {
-	return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
+	return (!opt->call_depth && opt->verbosity >= v) || opt->verbosity >= 5;
 }
 
 __attribute__((format (printf, 3, 4)))
-static void output(struct merge_options *o, int v, const char *fmt, ...)
+static void output(struct merge_options *opt, int v, const char *fmt, ...)
 {
 	va_list ap;
 
-	if (!show(o, v))
+	if (!show(opt, v))
 		return;
 
-	strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
+	strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);
 
 	va_start(ap, fmt);
-	strbuf_vaddf(&o->obuf, fmt, ap);
+	strbuf_vaddf(&opt->obuf, fmt, ap);
 	va_end(ap);
 
-	strbuf_addch(&o->obuf, '\n');
-	if (!o->buffer_output)
-		flush_output(o);
+	strbuf_addch(&opt->obuf, '\n');
+	if (!opt->buffer_output)
+		flush_output(opt);
 }
 
-static void output_commit_title(struct merge_options *o, struct commit *commit)
+static void output_commit_title(struct merge_options *opt, struct commit *commit)
 {
 	struct merge_remote_desc *desc;
 
-	strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
+	strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2);
 	desc = merge_remote_util(commit);
 	if (desc)
-		strbuf_addf(&o->obuf, "virtual %s\n", desc->name);
+		strbuf_addf(&opt->obuf, "virtual %s\n", desc->name);
 	else {
-		strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
+		strbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid,
 					 DEFAULT_ABBREV);
-		strbuf_addch(&o->obuf, ' ');
+		strbuf_addch(&opt->obuf, ' ');
 		if (parse_commit(commit) != 0)
-			strbuf_addstr(&o->obuf, _("(bad commit)\n"));
+			strbuf_addstr(&opt->obuf, _("(bad commit)\n"));
 		else {
 			const char *title;
 			const char *msg = get_commit_buffer(commit, NULL);
 			int len = find_commit_subject(msg, &title);
 			if (len)
-				strbuf_addf(&o->obuf, "%.*s\n", len, title);
+				strbuf_addf(&opt->obuf, "%.*s\n", len, title);
 			unuse_commit_buffer(commit, msg);
 		}
 	}
-	flush_output(o);
+	flush_output(opt);
 }
 
-static int add_cacheinfo(struct merge_options *o,
-			 unsigned int mode, const struct object_id *oid,
+static int add_cacheinfo(struct merge_options *opt,
+			 const struct diff_filespec *blob,
 			 const char *path, int stage, int refresh, int options)
 {
-	struct index_state *istate = o->repo->index;
+	struct index_state *istate = opt->repo->index;
 	struct cache_entry *ce;
 	int ret;
 
-	ce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0);
+	ce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0);
 	if (!ce)
-		return err(o, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
+		return err(opt, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
 
 	ret = add_index_entry(istate, ce, options);
 	if (refresh) {
@@ -361,7 +351,7 @@
 		nce = refresh_cache_entry(istate, ce,
 					  CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);
 		if (!nce)
-			return err(o, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
+			return err(opt, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
 		if (nce != ce)
 			ret = add_index_entry(istate, nce, options);
 	}
@@ -374,7 +364,7 @@
 	init_tree_desc(desc, tree->buffer, tree->size);
 }
 
-static int unpack_trees_start(struct merge_options *o,
+static int unpack_trees_start(struct merge_options *opt,
 			      struct tree *common,
 			      struct tree *head,
 			      struct tree *merge)
@@ -383,49 +373,49 @@
 	struct tree_desc t[3];
 	struct index_state tmp_index = { NULL };
 
-	memset(&o->unpack_opts, 0, sizeof(o->unpack_opts));
-	if (o->call_depth)
-		o->unpack_opts.index_only = 1;
+	memset(&opt->unpack_opts, 0, sizeof(opt->unpack_opts));
+	if (opt->call_depth)
+		opt->unpack_opts.index_only = 1;
 	else
-		o->unpack_opts.update = 1;
-	o->unpack_opts.merge = 1;
-	o->unpack_opts.head_idx = 2;
-	o->unpack_opts.fn = threeway_merge;
-	o->unpack_opts.src_index = o->repo->index;
-	o->unpack_opts.dst_index = &tmp_index;
-	o->unpack_opts.aggressive = !merge_detect_rename(o);
-	setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
+		opt->unpack_opts.update = 1;
+	opt->unpack_opts.merge = 1;
+	opt->unpack_opts.head_idx = 2;
+	opt->unpack_opts.fn = threeway_merge;
+	opt->unpack_opts.src_index = opt->repo->index;
+	opt->unpack_opts.dst_index = &tmp_index;
+	opt->unpack_opts.aggressive = !merge_detect_rename(opt);
+	setup_unpack_trees_porcelain(&opt->unpack_opts, "merge");
 
 	init_tree_desc_from_tree(t+0, common);
 	init_tree_desc_from_tree(t+1, head);
 	init_tree_desc_from_tree(t+2, merge);
 
-	rc = unpack_trees(3, t, &o->unpack_opts);
-	cache_tree_free(&o->repo->index->cache_tree);
+	rc = unpack_trees(3, t, &opt->unpack_opts);
+	cache_tree_free(&opt->repo->index->cache_tree);
 
 	/*
-	 * Update o->repo->index to match the new results, AFTER saving a copy
-	 * in o->orig_index.  Update src_index to point to the saved copy.
+	 * Update opt->repo->index to match the new results, AFTER saving a copy
+	 * in opt->orig_index.  Update src_index to point to the saved copy.
 	 * (verify_uptodate() checks src_index, and the original index is
 	 * the one that had the necessary modification timestamps.)
 	 */
-	o->orig_index = *o->repo->index;
-	*o->repo->index = tmp_index;
-	o->unpack_opts.src_index = &o->orig_index;
+	opt->orig_index = *opt->repo->index;
+	*opt->repo->index = tmp_index;
+	opt->unpack_opts.src_index = &opt->orig_index;
 
 	return rc;
 }
 
-static void unpack_trees_finish(struct merge_options *o)
+static void unpack_trees_finish(struct merge_options *opt)
 {
-	discard_index(&o->orig_index);
-	clear_unpack_trees_porcelain(&o->unpack_opts);
+	discard_index(&opt->orig_index);
+	clear_unpack_trees_porcelain(&opt->unpack_opts);
 }
 
-struct tree *write_tree_from_memory(struct merge_options *o)
+struct tree *write_tree_from_memory(struct merge_options *opt)
 {
 	struct tree *result = NULL;
-	struct index_state *istate = o->repo->index;
+	struct index_state *istate = opt->repo->index;
 
 	if (unmerged_index(istate)) {
 		int i;
@@ -444,11 +434,11 @@
 
 	if (!cache_tree_fully_valid(istate->cache_tree) &&
 	    cache_tree_update(istate, 0) < 0) {
-		err(o, _("error building trees"));
+		err(opt, _("error building trees"));
 		return NULL;
 	}
 
-	result = lookup_tree(o->repo, &istate->cache_tree->oid);
+	result = lookup_tree(opt->repo, &istate->cache_tree->oid);
 
 	return result;
 }
@@ -459,37 +449,36 @@
 {
 	struct path_hashmap_entry *entry;
 	int baselen = base->len;
-	struct merge_options *o = context;
+	struct merge_options *opt = context;
 
 	strbuf_addstr(base, path);
 
 	FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
 	hashmap_entry_init(entry, path_hash(entry->path));
-	hashmap_add(&o->current_file_dir_set, entry);
+	hashmap_add(&opt->current_file_dir_set, entry);
 
 	strbuf_setlen(base, baselen);
 	return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
-static void get_files_dirs(struct merge_options *o, struct tree *tree)
+static void get_files_dirs(struct merge_options *opt, struct tree *tree)
 {
 	struct pathspec match_all;
 	memset(&match_all, 0, sizeof(match_all));
 	read_tree_recursive(the_repository, tree, "", 0, 0,
-			    &match_all, save_files_dirs, o);
+			    &match_all, save_files_dirs, opt);
 }
 
 static int get_tree_entry_if_blob(const struct object_id *tree,
 				  const char *path,
-				  struct object_id *hashy,
-				  unsigned int *mode_o)
+				  struct diff_filespec *dfs)
 {
 	int ret;
 
-	ret = get_tree_entry(tree, path, hashy, mode_o);
-	if (S_ISDIR(*mode_o)) {
-		oidcpy(hashy, &null_oid);
-		*mode_o = 0;
+	ret = get_tree_entry(tree, path, &dfs->oid, &dfs->mode);
+	if (S_ISDIR(dfs->mode)) {
+		oidcpy(&dfs->oid, &null_oid);
+		dfs->mode = 0;
 	}
 	return ret;
 }
@@ -504,12 +493,9 @@
 {
 	struct string_list_item *item;
 	struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-	get_tree_entry_if_blob(&o->object.oid, path,
-			       &e->stages[1].oid, &e->stages[1].mode);
-	get_tree_entry_if_blob(&a->object.oid, path,
-			       &e->stages[2].oid, &e->stages[2].mode);
-	get_tree_entry_if_blob(&b->object.oid, path,
-			       &e->stages[3].oid, &e->stages[3].mode);
+	get_tree_entry_if_blob(&o->object.oid, path, &e->stages[1]);
+	get_tree_entry_if_blob(&a->object.oid, path, &e->stages[2]);
+	get_tree_entry_if_blob(&b->object.oid, path, &e->stages[3]);
 	item = string_list_insert(entries, path);
 	item->util = e;
 	return e;
@@ -573,7 +559,7 @@
 	return onelen - twolen;
 }
 
-static void record_df_conflict_files(struct merge_options *o,
+static void record_df_conflict_files(struct merge_options *opt,
 				     struct string_list *entries)
 {
 	/* If there is a D/F conflict and the file for such a conflict
@@ -598,7 +584,7 @@
 	 * If we're merging merge-bases, we don't want to bother with
 	 * any working directory changes.
 	 */
-	if (o->call_depth)
+	if (opt->call_depth)
 		return;
 
 	/* Ensure D/F conflicts are adjacent in the entries list. */
@@ -610,7 +596,7 @@
 	df_sorted_entries.cmp = string_list_df_name_compare;
 	string_list_sort(&df_sorted_entries);
 
-	string_list_clear(&o->df_conflict_file_set, 1);
+	string_list_clear(&opt->df_conflict_file_set, 1);
 	for (i = 0; i < df_sorted_entries.nr; i++) {
 		const char *path = df_sorted_entries.items[i].string;
 		int len = strlen(path);
@@ -626,7 +612,7 @@
 		    len > last_len &&
 		    memcmp(path, last_file, last_len) == 0 &&
 		    path[last_len] == '/') {
-			string_list_insert(&o->df_conflict_file_set, last_file);
+			string_list_insert(&opt->df_conflict_file_set, last_file);
 		}
 
 		/*
@@ -645,33 +631,6 @@
 	string_list_clear(&df_sorted_entries, 0);
 }
 
-struct rename {
-	struct diff_filepair *pair;
-	/*
-	 * Purpose of src_entry and dst_entry:
-	 *
-	 * If 'before' is renamed to 'after' then src_entry will contain
-	 * the versions of 'before' from the merge_base, HEAD, and MERGE in
-	 * stages 1, 2, and 3; dst_entry will contain the respective
-	 * versions of 'after' in corresponding locations.  Thus, we have a
-	 * total of six modes and oids, though some will be null.  (Stage 0
-	 * is ignored; we're interested in handling conflicts.)
-	 *
-	 * Since we don't turn on break-rewrites by default, neither
-	 * src_entry nor dst_entry can have all three of their stages have
-	 * non-null oids, meaning at most four of the six will be non-null.
-	 * Also, since this is a rename, both src_entry and dst_entry will
-	 * have at least one non-null oid, meaning at least two will be
-	 * non-null.  Of the six oids, a typical rename will have three be
-	 * non-null.  Only two implies a rename/delete, and four implies a
-	 * rename/add.
-	 */
-	struct stage_data *src_entry;
-	struct stage_data *dst_entry;
-	unsigned add_turned_into_rename:1;
-	unsigned processed:1;
-};
-
 static int update_stages(struct merge_options *opt, const char *path,
 			 const struct diff_filespec *o,
 			 const struct diff_filespec *a,
@@ -692,13 +651,13 @@
 		if (remove_file_from_index(opt->repo->index, path))
 			return -1;
 	if (o)
-		if (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))
+		if (add_cacheinfo(opt, o, path, 1, 0, options))
 			return -1;
 	if (a)
-		if (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))
+		if (add_cacheinfo(opt, a, path, 2, 0, options))
 			return -1;
 	if (b)
-		if (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))
+		if (add_cacheinfo(opt, b, path, 3, 0, options))
 			return -1;
 	return 0;
 }
@@ -717,20 +676,20 @@
 	oidcpy(&entry->stages[3].oid, &b->oid);
 }
 
-static int remove_file(struct merge_options *o, int clean,
+static int remove_file(struct merge_options *opt, int clean,
 		       const char *path, int no_wd)
 {
-	int update_cache = o->call_depth || clean;
-	int update_working_directory = !o->call_depth && !no_wd;
+	int update_cache = opt->call_depth || clean;
+	int update_working_directory = !opt->call_depth && !no_wd;
 
 	if (update_cache) {
-		if (remove_file_from_index(o->repo->index, path))
+		if (remove_file_from_index(opt->repo->index, path))
 			return -1;
 	}
 	if (update_working_directory) {
 		if (ignore_case) {
 			struct cache_entry *ce;
-			ce = index_file_exists(o->repo->index, path, strlen(path),
+			ce = index_file_exists(opt->repo->index, path, strlen(path),
 					       ignore_case);
 			if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))
 				return 0;
@@ -751,7 +710,7 @@
 			out->buf[i] = '_';
 }
 
-static char *unique_path(struct merge_options *o, const char *path, const char *branch)
+static char *unique_path(struct merge_options *opt, const char *path, const char *branch)
 {
 	struct path_hashmap_entry *entry;
 	struct strbuf newpath = STRBUF_INIT;
@@ -762,16 +721,16 @@
 	add_flattened_path(&newpath, branch);
 
 	base_len = newpath.len;
-	while (hashmap_get_from_hash(&o->current_file_dir_set,
+	while (hashmap_get_from_hash(&opt->current_file_dir_set,
 				     path_hash(newpath.buf), newpath.buf) ||
-	       (!o->call_depth && file_exists(newpath.buf))) {
+	       (!opt->call_depth && file_exists(newpath.buf))) {
 		strbuf_setlen(&newpath, base_len);
 		strbuf_addf(&newpath, "_%d", suffix++);
 	}
 
 	FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
 	hashmap_entry_init(entry, path_hash(entry->path));
-	hashmap_add(&o->current_file_dir_set, entry);
+	hashmap_add(&opt->current_file_dir_set, entry);
 	return strbuf_detach(&newpath, NULL);
 }
 
@@ -810,10 +769,10 @@
  * Returns whether path was tracked in the index before the merge started,
  * and its oid and mode match the specified values
  */
-static int was_tracked_and_matches(struct merge_options *o, const char *path,
-				   const struct object_id *oid, unsigned mode)
+static int was_tracked_and_matches(struct merge_options *opt, const char *path,
+				   const struct diff_filespec *blob)
 {
-	int pos = index_name_pos(&o->orig_index, path, strlen(path));
+	int pos = index_name_pos(&opt->orig_index, path, strlen(path));
 	struct cache_entry *ce;
 
 	if (0 > pos)
@@ -821,16 +780,16 @@
 		return 0;
 
 	/* See if the file we were tracking before matches */
-	ce = o->orig_index.cache[pos];
-	return (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);
+	ce = opt->orig_index.cache[pos];
+	return (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);
 }
 
 /*
  * Returns whether path was tracked in the index before the merge started
  */
-static int was_tracked(struct merge_options *o, const char *path)
+static int was_tracked(struct merge_options *opt, const char *path)
 {
-	int pos = index_name_pos(&o->orig_index, path, strlen(path));
+	int pos = index_name_pos(&opt->orig_index, path, strlen(path));
 
 	if (0 <= pos)
 		/* we were tracking this path before the merge */
@@ -839,13 +798,13 @@
 	return 0;
 }
 
-static int would_lose_untracked(struct merge_options *o, const char *path)
+static int would_lose_untracked(struct merge_options *opt, const char *path)
 {
-	struct index_state *istate = o->repo->index;
+	struct index_state *istate = opt->repo->index;
 
 	/*
 	 * This may look like it can be simplified to:
-	 *   return !was_tracked(o, path) && file_exists(path)
+	 *   return !was_tracked(opt, path) && file_exists(path)
 	 * but it can't.  This function needs to know whether path was in
 	 * the working tree due to EITHER having been tracked in the index
 	 * before the merge OR having been put into the working copy and
@@ -882,38 +841,38 @@
 	return file_exists(path);
 }
 
-static int was_dirty(struct merge_options *o, const char *path)
+static int was_dirty(struct merge_options *opt, const char *path)
 {
 	struct cache_entry *ce;
 	int dirty = 1;
 
-	if (o->call_depth || !was_tracked(o, path))
+	if (opt->call_depth || !was_tracked(opt, path))
 		return !dirty;
 
-	ce = index_file_exists(o->unpack_opts.src_index,
+	ce = index_file_exists(opt->unpack_opts.src_index,
 			       path, strlen(path), ignore_case);
-	dirty = verify_uptodate(ce, &o->unpack_opts) != 0;
+	dirty = verify_uptodate(ce, &opt->unpack_opts) != 0;
 	return dirty;
 }
 
-static int make_room_for_path(struct merge_options *o, const char *path)
+static int make_room_for_path(struct merge_options *opt, const char *path)
 {
 	int status, i;
 	const char *msg = _("failed to create path '%s'%s");
 
 	/* Unlink any D/F conflict files that are in the way */
-	for (i = 0; i < o->df_conflict_file_set.nr; i++) {
-		const char *df_path = o->df_conflict_file_set.items[i].string;
+	for (i = 0; i < opt->df_conflict_file_set.nr; i++) {
+		const char *df_path = opt->df_conflict_file_set.items[i].string;
 		size_t pathlen = strlen(path);
 		size_t df_pathlen = strlen(df_path);
 		if (df_pathlen < pathlen &&
 		    path[df_pathlen] == '/' &&
 		    strncmp(path, df_path, df_pathlen) == 0) {
-			output(o, 3,
+			output(opt, 3,
 			       _("Removing %s to make room for subdirectory\n"),
 			       df_path);
 			unlink(df_path);
-			unsorted_string_list_delete_item(&o->df_conflict_file_set,
+			unsorted_string_list_delete_item(&opt->df_conflict_file_set,
 							 i, 0);
 			break;
 		}
@@ -924,16 +883,16 @@
 	if (status) {
 		if (status == SCLD_EXISTS)
 			/* something else exists */
-			return err(o, msg, path, _(": perhaps a D/F conflict?"));
-		return err(o, msg, path, "");
+			return err(opt, msg, path, _(": perhaps a D/F conflict?"));
+		return err(opt, msg, path, "");
 	}
 
 	/*
 	 * Do not unlink a file in the work tree if we are not
 	 * tracking it.
 	 */
-	if (would_lose_untracked(o, path))
-		return err(o, _("refusing to lose untracked file at '%s'"),
+	if (would_lose_untracked(opt, path))
+		return err(opt, _("refusing to lose untracked file at '%s'"),
 			   path);
 
 	/* Successful unlink is good.. */
@@ -943,19 +902,18 @@
 	if (errno == ENOENT)
 		return 0;
 	/* .. but not some other error (who really cares what?) */
-	return err(o, msg, path, _(": perhaps a D/F conflict?"));
+	return err(opt, msg, path, _(": perhaps a D/F conflict?"));
 }
 
-static int update_file_flags(struct merge_options *o,
-			     const struct object_id *oid,
-			     unsigned mode,
+static int update_file_flags(struct merge_options *opt,
+			     const struct diff_filespec *contents,
 			     const char *path,
 			     int update_cache,
 			     int update_wd)
 {
 	int ret = 0;
 
-	if (o->call_depth)
+	if (opt->call_depth)
 		update_wd = 0;
 
 	if (update_wd) {
@@ -963,7 +921,7 @@
 		void *buf;
 		unsigned long size;
 
-		if (S_ISGITLINK(mode)) {
+		if (S_ISGITLINK(contents->mode)) {
 			/*
 			 * We may later decide to recursively descend into
 			 * the submodule directory and update its index
@@ -973,84 +931,84 @@
 			goto update_index;
 		}
 
-		buf = read_object_file(oid, &type, &size);
+		buf = read_object_file(&contents->oid, &type, &size);
 		if (!buf)
-			return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
+			return err(opt, _("cannot read object %s '%s'"),
+				   oid_to_hex(&contents->oid), path);
 		if (type != OBJ_BLOB) {
-			ret = err(o, _("blob expected for %s '%s'"), oid_to_hex(oid), path);
+			ret = err(opt, _("blob expected for %s '%s'"),
+				  oid_to_hex(&contents->oid), path);
 			goto free_buf;
 		}
-		if (S_ISREG(mode)) {
+		if (S_ISREG(contents->mode)) {
 			struct strbuf strbuf = STRBUF_INIT;
-			if (convert_to_working_tree(o->repo->index, path, buf, size, &strbuf)) {
+			if (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) {
 				free(buf);
 				size = strbuf.len;
 				buf = strbuf_detach(&strbuf, NULL);
 			}
 		}
 
-		if (make_room_for_path(o, path) < 0) {
+		if (make_room_for_path(opt, path) < 0) {
 			update_wd = 0;
 			goto free_buf;
 		}
-		if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
+		if (S_ISREG(contents->mode) ||
+		    (!has_symlinks && S_ISLNK(contents->mode))) {
 			int fd;
-			if (mode & 0100)
-				mode = 0777;
-			else
-				mode = 0666;
+			int mode = (contents->mode & 0100 ? 0777 : 0666);
+
 			fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
 			if (fd < 0) {
-				ret = err(o, _("failed to open '%s': %s"),
+				ret = err(opt, _("failed to open '%s': %s"),
 					  path, strerror(errno));
 				goto free_buf;
 			}
 			write_in_full(fd, buf, size);
 			close(fd);
-		} else if (S_ISLNK(mode)) {
+		} else if (S_ISLNK(contents->mode)) {
 			char *lnk = xmemdupz(buf, size);
 			safe_create_leading_directories_const(path);
 			unlink(path);
 			if (symlink(lnk, path))
-				ret = err(o, _("failed to symlink '%s': %s"),
+				ret = err(opt, _("failed to symlink '%s': %s"),
 					  path, strerror(errno));
 			free(lnk);
 		} else
-			ret = err(o,
+			ret = err(opt,
 				  _("do not know what to do with %06o %s '%s'"),
-				  mode, oid_to_hex(oid), path);
+				  contents->mode, oid_to_hex(&contents->oid), path);
 	free_buf:
 		free(buf);
 	}
 update_index:
 	if (!ret && update_cache)
-		if (add_cacheinfo(o, mode, oid, path, 0, update_wd,
+		if (add_cacheinfo(opt, contents, path, 0, update_wd,
 				  ADD_CACHE_OK_TO_ADD))
 			return -1;
 	return ret;
 }
 
-static int update_file(struct merge_options *o,
+static int update_file(struct merge_options *opt,
 		       int clean,
-		       const struct object_id *oid,
-		       unsigned mode,
+		       const struct diff_filespec *contents,
 		       const char *path)
 {
-	return update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);
+	return update_file_flags(opt, contents, path,
+				 opt->call_depth || clean, !opt->call_depth);
 }
 
 /* Low level file merging, update and removal */
 
 struct merge_file_info {
-	struct object_id oid;
-	unsigned mode;
+	struct diff_filespec blob; /* mostly use oid & mode; sometimes path */
 	unsigned clean:1,
 		 merge:1;
 };
 
-static int merge_3way(struct merge_options *o,
+static int merge_3way(struct merge_options *opt,
 		      mmbuffer_t *result_buf,
-		      const struct diff_filespec *one,
+		      const struct diff_filespec *o,
 		      const struct diff_filespec *a,
 		      const struct diff_filespec *b,
 		      const char *branch1,
@@ -1062,15 +1020,15 @@
 	char *base_name, *name1, *name2;
 	int merge_status;
 
-	ll_opts.renormalize = o->renormalize;
+	ll_opts.renormalize = opt->renormalize;
 	ll_opts.extra_marker_size = extra_marker_size;
-	ll_opts.xdl_opts = o->xdl_opts;
+	ll_opts.xdl_opts = opt->xdl_opts;
 
-	if (o->call_depth) {
+	if (opt->call_depth) {
 		ll_opts.virtual_ancestor = 1;
 		ll_opts.variant = 0;
 	} else {
-		switch (o->recursive_variant) {
+		switch (opt->recursive_variant) {
 		case MERGE_RECURSIVE_OURS:
 			ll_opts.variant = XDL_MERGE_FAVOR_OURS;
 			break;
@@ -1083,26 +1041,27 @@
 		}
 	}
 
+	assert(a->path && b->path);
 	if (strcmp(a->path, b->path) ||
-	    (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {
-		base_name = o->ancestor == NULL ? NULL :
-			mkpathdup("%s:%s", o->ancestor, one->path);
+	    (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) {
+		base_name = opt->ancestor == NULL ? NULL :
+			mkpathdup("%s:%s", opt->ancestor, o->path);
 		name1 = mkpathdup("%s:%s", branch1, a->path);
 		name2 = mkpathdup("%s:%s", branch2, b->path);
 	} else {
-		base_name = o->ancestor == NULL ? NULL :
-			mkpathdup("%s", o->ancestor);
+		base_name = opt->ancestor == NULL ? NULL :
+			mkpathdup("%s", opt->ancestor);
 		name1 = mkpathdup("%s", branch1);
 		name2 = mkpathdup("%s", branch2);
 	}
 
-	read_mmblob(&orig, &one->oid);
+	read_mmblob(&orig, &o->oid);
 	read_mmblob(&src1, &a->oid);
 	read_mmblob(&src2, &b->oid);
 
 	merge_status = ll_merge(result_buf, a->path, &orig, base_name,
 				&src1, name1, &src2, name2,
-				o->repo->index, &ll_opts);
+				opt->repo->index, &ll_opts);
 
 	free(base_name);
 	free(name1);
@@ -1122,7 +1081,7 @@
 	struct commit *commit;
 	int contains_another;
 
-	char merged_revision[42];
+	char merged_revision[GIT_MAX_HEXSZ + 2];
 	const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
 				   "--all", merged_revision, NULL };
 	struct rev_info revs;
@@ -1184,7 +1143,12 @@
 	strbuf_release(&sb);
 }
 
-static int merge_submodule(struct merge_options *o,
+static int is_valid(const struct diff_filespec *dfs)
+{
+	return dfs->mode != 0 && !is_null_oid(&dfs->oid);
+}
+
+static int merge_submodule(struct merge_options *opt,
 			   struct object_id *result, const char *path,
 			   const struct object_id *base, const struct object_id *a,
 			   const struct object_id *b)
@@ -1194,7 +1158,7 @@
 	struct object_array merges;
 
 	int i;
-	int search = !o->call_depth;
+	int search = !opt->call_depth;
 
 	/* store a in result in case we fail */
 	oidcpy(result, a);
@@ -1208,32 +1172,32 @@
 		return 0;
 
 	if (add_submodule_odb(path)) {
-		output(o, 1, _("Failed to merge submodule %s (not checked out)"), path);
+		output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
 		return 0;
 	}
 
-	if (!(commit_base = lookup_commit_reference(o->repo, base)) ||
-	    !(commit_a = lookup_commit_reference(o->repo, a)) ||
-	    !(commit_b = lookup_commit_reference(o->repo, b))) {
-		output(o, 1, _("Failed to merge submodule %s (commits not present)"), path);
+	if (!(commit_base = lookup_commit_reference(opt->repo, base)) ||
+	    !(commit_a = lookup_commit_reference(opt->repo, a)) ||
+	    !(commit_b = lookup_commit_reference(opt->repo, b))) {
+		output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path);
 		return 0;
 	}
 
 	/* check whether both changes are forward */
 	if (!in_merge_bases(commit_base, commit_a) ||
 	    !in_merge_bases(commit_base, commit_b)) {
-		output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
+		output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
 		return 0;
 	}
 
 	/* Case #1: a is contained in b or vice versa */
 	if (in_merge_bases(commit_a, commit_b)) {
 		oidcpy(result, b);
-		if (show(o, 3)) {
-			output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-			output_commit_title(o, commit_b);
-		} else if (show(o, 2))
-			output(o, 2, _("Fast-forwarding submodule %s"), path);
+		if (show(opt, 3)) {
+			output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
+			output_commit_title(opt, commit_b);
+		} else if (show(opt, 2))
+			output(opt, 2, _("Fast-forwarding submodule %s"), path);
 		else
 			; /* no output */
 
@@ -1241,11 +1205,11 @@
 	}
 	if (in_merge_bases(commit_b, commit_a)) {
 		oidcpy(result, a);
-		if (show(o, 3)) {
-			output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
-			output_commit_title(o, commit_a);
-		} else if (show(o, 2))
-			output(o, 2, _("Fast-forwarding submodule %s"), path);
+		if (show(opt, 3)) {
+			output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
+			output_commit_title(opt, commit_a);
+		} else if (show(opt, 2))
+			output(opt, 2, _("Fast-forwarding submodule %s"), path);
 		else
 			; /* no output */
 
@@ -1264,18 +1228,18 @@
 		return 0;
 
 	/* find commit which merges them */
-	parent_count = find_first_merges(o->repo, &merges, path,
+	parent_count = find_first_merges(opt->repo, &merges, path,
 					 commit_a, commit_b);
 	switch (parent_count) {
 	case 0:
-		output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
+		output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
 		break;
 
 	case 1:
-		output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
-		output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
+		output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
+		output(opt, 2, _("Found a possible merge resolution for the submodule:\n"));
 		print_commit((struct commit *) merges.objects[0].item);
-		output(o, 2, _(
+		output(opt, 2, _(
 		       "If this is correct simply add it to the index "
 		       "for example\n"
 		       "by using:\n\n"
@@ -1285,7 +1249,7 @@
 		break;
 
 	default:
-		output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
+		output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
 		for (i = 0; i < merges.nr; i++)
 			print_commit((struct commit *) merges.objects[i].item);
 	}
@@ -1294,8 +1258,8 @@
 	return 0;
 }
 
-static int merge_mode_and_contents(struct merge_options *o,
-				   const struct diff_filespec *one,
+static int merge_mode_and_contents(struct merge_options *opt,
+				   const struct diff_filespec *o,
 				   const struct diff_filespec *a,
 				   const struct diff_filespec *b,
 				   const char *filename,
@@ -1304,13 +1268,13 @@
 				   const int extra_marker_size,
 				   struct merge_file_info *result)
 {
-	if (o->branch1 != branch1) {
+	if (opt->branch1 != branch1) {
 		/*
 		 * It's weird getting a reverse merge with HEAD on the bottom
 		 * side of the conflict markers and the other branch on the
 		 * top.  Fix that.
 		 */
-		return merge_mode_and_contents(o, one, b, a,
+		return merge_mode_and_contents(opt, o, b, a,
 					       filename,
 					       branch2, branch1,
 					       extra_marker_size, result);
@@ -1322,48 +1286,48 @@
 	if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
 		result->clean = 0;
 		if (S_ISREG(a->mode)) {
-			result->mode = a->mode;
-			oidcpy(&result->oid, &a->oid);
+			result->blob.mode = a->mode;
+			oidcpy(&result->blob.oid, &a->oid);
 		} else {
-			result->mode = b->mode;
-			oidcpy(&result->oid, &b->oid);
+			result->blob.mode = b->mode;
+			oidcpy(&result->blob.oid, &b->oid);
 		}
 	} else {
-		if (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))
+		if (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))
 			result->merge = 1;
 
 		/*
 		 * Merge modes
 		 */
-		if (a->mode == b->mode || a->mode == one->mode)
-			result->mode = b->mode;
+		if (a->mode == b->mode || a->mode == o->mode)
+			result->blob.mode = b->mode;
 		else {
-			result->mode = a->mode;
-			if (b->mode != one->mode) {
+			result->blob.mode = a->mode;
+			if (b->mode != o->mode) {
 				result->clean = 0;
 				result->merge = 1;
 			}
 		}
 
-		if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))
-			oidcpy(&result->oid, &b->oid);
-		else if (oid_eq(&b->oid, &one->oid))
-			oidcpy(&result->oid, &a->oid);
+		if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))
+			oidcpy(&result->blob.oid, &b->oid);
+		else if (oid_eq(&b->oid, &o->oid))
+			oidcpy(&result->blob.oid, &a->oid);
 		else if (S_ISREG(a->mode)) {
 			mmbuffer_t result_buf;
 			int ret = 0, merge_status;
 
-			merge_status = merge_3way(o, &result_buf, one, a, b,
+			merge_status = merge_3way(opt, &result_buf, o, a, b,
 						  branch1, branch2,
 						  extra_marker_size);
 
 			if ((merge_status < 0) || !result_buf.ptr)
-				ret = err(o, _("Failed to execute internal merge"));
+				ret = err(opt, _("Failed to execute internal merge"));
 
 			if (!ret &&
 			    write_object_file(result_buf.ptr, result_buf.size,
-					      blob_type, &result->oid))
-				ret = err(o, _("Unable to add %s to database"),
+					      blob_type, &result->blob.oid))
+				ret = err(opt, _("Unable to add %s to database"),
 					  a->path);
 
 			free(result_buf.ptr);
@@ -1371,23 +1335,23 @@
 				return ret;
 			result->clean = (merge_status == 0);
 		} else if (S_ISGITLINK(a->mode)) {
-			result->clean = merge_submodule(o, &result->oid,
-							one->path,
-							&one->oid,
+			result->clean = merge_submodule(opt, &result->blob.oid,
+							o->path,
+							&o->oid,
 							&a->oid,
 							&b->oid);
 		} else if (S_ISLNK(a->mode)) {
-			switch (o->recursive_variant) {
+			switch (opt->recursive_variant) {
 			case MERGE_RECURSIVE_NORMAL:
-				oidcpy(&result->oid, &a->oid);
+				oidcpy(&result->blob.oid, &a->oid);
 				if (!oid_eq(&a->oid, &b->oid))
 					result->clean = 0;
 				break;
 			case MERGE_RECURSIVE_OURS:
-				oidcpy(&result->oid, &a->oid);
+				oidcpy(&result->blob.oid, &a->oid);
 				break;
 			case MERGE_RECURSIVE_THEIRS:
-				oidcpy(&result->oid, &b->oid);
+				oidcpy(&result->blob.oid, &b->oid);
 				break;
 			}
 		} else
@@ -1395,14 +1359,13 @@
 	}
 
 	if (result->merge)
-		output(o, 2, _("Auto-merging %s"), filename);
+		output(opt, 2, _("Auto-merging %s"), filename);
 
 	return 0;
 }
 
-static int handle_rename_via_dir(struct merge_options *o,
-				 struct diff_filepair *pair,
-				 const char *rename_branch)
+static int handle_rename_via_dir(struct merge_options *opt,
+				 struct rename_conflict_info *ci)
 {
 	/*
 	 * Handle file adds that need to be renamed due to directory rename
@@ -1410,38 +1373,47 @@
 	 * there is no content merge to do; just move the file into the
 	 * desired final location.
 	 */
-	const struct diff_filespec *dest = pair->two;
+	const struct rename *ren = ci->ren1;
+	const struct diff_filespec *dest = ren->pair->two;
+	char *file_path = dest->path;
+	int mark_conflicted = (opt->detect_directory_renames == 1);
+	assert(ren->dir_rename_original_dest);
 
-	if (!o->call_depth && would_lose_untracked(o, dest->path)) {
-		char *alt_path = unique_path(o, dest->path, rename_branch);
-
-		output(o, 1, _("Error: Refusing to lose untracked file at %s; "
-			       "writing to %s instead."),
-		       dest->path, alt_path);
-		/*
-		 * Write the file in worktree at alt_path, but not in the
-		 * index.  Instead, write to dest->path for the index but
-		 * only at the higher appropriate stage.
-		 */
-		if (update_file(o, 0, &dest->oid, dest->mode, alt_path))
-			return -1;
-		free(alt_path);
-		return update_stages(o, dest->path, NULL,
-				     rename_branch == o->branch1 ? dest : NULL,
-				     rename_branch == o->branch1 ? NULL : dest);
+	if (!opt->call_depth && would_lose_untracked(opt, dest->path)) {
+		mark_conflicted = 1;
+		file_path = unique_path(opt, dest->path, ren->branch);
+		output(opt, 1, _("Error: Refusing to lose untracked file at %s; "
+				 "writing to %s instead."),
+		       dest->path, file_path);
 	}
 
-	/* Update dest->path both in index and in worktree */
-	if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
-		return -1;
-	return 0;
+	if (mark_conflicted) {
+		/*
+		 * Write the file in worktree at file_path.  In the index,
+		 * only record the file at dest->path in the appropriate
+		 * higher stage.
+		 */
+		if (update_file(opt, 0, dest, file_path))
+			return -1;
+		if (file_path != dest->path)
+			free(file_path);
+		if (update_stages(opt, dest->path, NULL,
+				  ren->branch == opt->branch1 ? dest : NULL,
+				  ren->branch == opt->branch1 ? NULL : dest))
+			return -1;
+		return 0; /* not clean, but conflicted */
+	} else {
+		/* Update dest->path both in index and in worktree */
+		if (update_file(opt, 1, dest, dest->path))
+			return -1;
+		return 1; /* clean */
+	}
 }
 
-static int handle_change_delete(struct merge_options *o,
+static int handle_change_delete(struct merge_options *opt,
 				const char *path, const char *old_path,
-				const struct object_id *o_oid, int o_mode,
-				const struct object_id *changed_oid,
-				int changed_mode,
+				const struct diff_filespec *o,
+				const struct diff_filespec *changed,
 				const char *change_branch,
 				const char *delete_branch,
 				const char *change, const char *change_past)
@@ -1450,20 +1422,20 @@
 	const char *update_path = path;
 	int ret = 0;
 
-	if (dir_in_way(o->repo->index, path, !o->call_depth, 0) ||
-	    (!o->call_depth && would_lose_untracked(o, path))) {
-		update_path = alt_path = unique_path(o, path, change_branch);
+	if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0) ||
+	    (!opt->call_depth && would_lose_untracked(opt, path))) {
+		update_path = alt_path = unique_path(opt, path, change_branch);
 	}
 
-	if (o->call_depth) {
+	if (opt->call_depth) {
 		/*
 		 * We cannot arbitrarily accept either a_sha or b_sha as
 		 * correct; since there is no true "middle point" between
 		 * them, simply reuse the base version for virtual merge base.
 		 */
-		ret = remove_file_from_index(o->repo->index, path);
+		ret = remove_file_from_index(opt->repo->index, path);
 		if (!ret)
-			ret = update_file(o, 0, o_oid, o_mode, update_path);
+			ret = update_file(opt, 0, o, update_path);
 	} else {
 		/*
 		 * Despite the four nearly duplicate messages and argument
@@ -1482,24 +1454,24 @@
 		 */
 		if (!alt_path) {
 			if (!old_path) {
-				output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+				output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
 				       "and %s in %s. Version %s of %s left in tree."),
 				       change, path, delete_branch, change_past,
 				       change_branch, change_branch, path);
 			} else {
-				output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+				output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
 				       "and %s to %s in %s. Version %s of %s left in tree."),
 				       change, old_path, delete_branch, change_past, path,
 				       change_branch, change_branch, path);
 			}
 		} else {
 			if (!old_path) {
-				output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+				output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
 				       "and %s in %s. Version %s of %s left in tree at %s."),
 				       change, path, delete_branch, change_past,
 				       change_branch, change_branch, path, alt_path);
 			} else {
-				output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+				output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
 				       "and %s to %s in %s. Version %s of %s left in tree at %s."),
 				       change, old_path, delete_branch, change_past, path,
 				       change_branch, change_branch, path, alt_path);
@@ -1507,142 +1479,127 @@
 		}
 		/*
 		 * No need to call update_file() on path when change_branch ==
-		 * o->branch1 && !alt_path, since that would needlessly touch
+		 * opt->branch1 && !alt_path, since that would needlessly touch
 		 * path.  We could call update_file_flags() with update_cache=0
 		 * and update_wd=0, but that's a no-op.
 		 */
-		if (change_branch != o->branch1 || alt_path)
-			ret = update_file(o, 0, changed_oid, changed_mode, update_path);
+		if (change_branch != opt->branch1 || alt_path)
+			ret = update_file(opt, 0, changed, update_path);
 	}
 	free(alt_path);
 
 	return ret;
 }
 
-static int handle_rename_delete(struct merge_options *o,
-				struct diff_filepair *pair,
-				const char *rename_branch,
-				const char *delete_branch)
+static int handle_rename_delete(struct merge_options *opt,
+				struct rename_conflict_info *ci)
 {
-	const struct diff_filespec *orig = pair->one;
-	const struct diff_filespec *dest = pair->two;
+	const struct rename *ren = ci->ren1;
+	const struct diff_filespec *orig = ren->pair->one;
+	const struct diff_filespec *dest = ren->pair->two;
+	const char *rename_branch = ren->branch;
+	const char *delete_branch = (opt->branch1 == ren->branch ?
+				     opt->branch2 : opt->branch1);
 
-	if (handle_change_delete(o,
-				 o->call_depth ? orig->path : dest->path,
-				 o->call_depth ? NULL : orig->path,
-				 &orig->oid, orig->mode,
-				 &dest->oid, dest->mode,
+	if (handle_change_delete(opt,
+				 opt->call_depth ? orig->path : dest->path,
+				 opt->call_depth ? NULL : orig->path,
+				 orig, dest,
 				 rename_branch, delete_branch,
 				 _("rename"), _("renamed")))
 		return -1;
 
-	if (o->call_depth)
-		return remove_file_from_index(o->repo->index, dest->path);
+	if (opt->call_depth)
+		return remove_file_from_index(opt->repo->index, dest->path);
 	else
-		return update_stages(o, dest->path, NULL,
-				     rename_branch == o->branch1 ? dest : NULL,
-				     rename_branch == o->branch1 ? NULL : dest);
+		return update_stages(opt, dest->path, NULL,
+				     rename_branch == opt->branch1 ? dest : NULL,
+				     rename_branch == opt->branch1 ? NULL : dest);
 }
 
-static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
-						 struct stage_data *entry,
-						 int stage)
-{
-	struct object_id *oid = &entry->stages[stage].oid;
-	unsigned mode = entry->stages[stage].mode;
-	if (mode == 0 || is_null_oid(oid))
-		return NULL;
-	oidcpy(&target->oid, oid);
-	target->mode = mode;
-	return target;
-}
-
-static int handle_file_collision(struct merge_options *o,
+static int handle_file_collision(struct merge_options *opt,
 				 const char *collide_path,
 				 const char *prev_path1,
 				 const char *prev_path2,
 				 const char *branch1, const char *branch2,
-				 const struct object_id *a_oid,
-				 unsigned int a_mode,
-				 const struct object_id *b_oid,
-				 unsigned int b_mode)
+				 struct diff_filespec *a,
+				 struct diff_filespec *b)
 {
 	struct merge_file_info mfi;
-	struct diff_filespec null, a, b;
+	struct diff_filespec null;
 	char *alt_path = NULL;
 	const char *update_path = collide_path;
 
 	/*
 	 * It's easiest to get the correct things into stage 2 and 3, and
 	 * to make sure that the content merge puts HEAD before the other
-	 * branch if we just ensure that branch1 == o->branch1.  So, simply
+	 * branch if we just ensure that branch1 == opt->branch1.  So, simply
 	 * flip arguments around if we don't have that.
 	 */
-	if (branch1 != o->branch1) {
-		return handle_file_collision(o, collide_path,
+	if (branch1 != opt->branch1) {
+		return handle_file_collision(opt, collide_path,
 					     prev_path2, prev_path1,
 					     branch2, branch1,
-					     b_oid, b_mode,
-					     a_oid, a_mode);
+					     b, a);
 	}
 
 	/*
 	 * In the recursive case, we just opt to undo renames
 	 */
-	if (o->call_depth && (prev_path1 || prev_path2)) {
-		/* Put first file (a_oid, a_mode) in its original spot */
+	if (opt->call_depth && (prev_path1 || prev_path2)) {
+		/* Put first file (a->oid, a->mode) in its original spot */
 		if (prev_path1) {
-			if (update_file(o, 1, a_oid, a_mode, prev_path1))
+			if (update_file(opt, 1, a, prev_path1))
 				return -1;
 		} else {
-			if (update_file(o, 1, a_oid, a_mode, collide_path))
+			if (update_file(opt, 1, a, collide_path))
 				return -1;
 		}
 
-		/* Put second file (b_oid, b_mode) in its original spot */
+		/* Put second file (b->oid, b->mode) in its original spot */
 		if (prev_path2) {
-			if (update_file(o, 1, b_oid, b_mode, prev_path2))
+			if (update_file(opt, 1, b, prev_path2))
 				return -1;
 		} else {
-			if (update_file(o, 1, b_oid, b_mode, collide_path))
+			if (update_file(opt, 1, b, collide_path))
 				return -1;
 		}
 
 		/* Don't leave something at collision path if unrenaming both */
 		if (prev_path1 && prev_path2)
-			remove_file(o, 1, collide_path, 0);
+			remove_file(opt, 1, collide_path, 0);
 
 		return 0;
 	}
 
 	/* Remove rename sources if rename/add or rename/rename(2to1) */
 	if (prev_path1)
-		remove_file(o, 1, prev_path1,
-			    o->call_depth || would_lose_untracked(o, prev_path1));
+		remove_file(opt, 1, prev_path1,
+			    opt->call_depth || would_lose_untracked(opt, prev_path1));
 	if (prev_path2)
-		remove_file(o, 1, prev_path2,
-			    o->call_depth || would_lose_untracked(o, prev_path2));
+		remove_file(opt, 1, prev_path2,
+			    opt->call_depth || would_lose_untracked(opt, prev_path2));
 
 	/*
 	 * Remove the collision path, if it wouldn't cause dirty contents
 	 * or an untracked file to get lost.  We'll either overwrite with
 	 * merged contents, or just write out to differently named files.
 	 */
-	if (was_dirty(o, collide_path)) {
-		output(o, 1, _("Refusing to lose dirty file at %s"),
+	if (was_dirty(opt, collide_path)) {
+		output(opt, 1, _("Refusing to lose dirty file at %s"),
 		       collide_path);
-		update_path = alt_path = unique_path(o, collide_path, "merged");
-	} else if (would_lose_untracked(o, collide_path)) {
+		update_path = alt_path = unique_path(opt, collide_path, "merged");
+	} else if (would_lose_untracked(opt, collide_path)) {
 		/*
 		 * Only way we get here is if both renames were from
 		 * a directory rename AND user had an untracked file
 		 * at the location where both files end up after the
 		 * two directory renames.  See testcase 10d of t6043.
 		 */
-		output(o, 1, _("Refusing to lose untracked file at "
+		output(opt, 1, _("Refusing to lose untracked file at "
 			       "%s, even though it's in the way."),
 		       collide_path);
-		update_path = alt_path = unique_path(o, collide_path, "merged");
+		update_path = alt_path = unique_path(opt, collide_path, "merged");
 	} else {
 		/*
 		 * FIXME: It's possible that the two files are identical
@@ -1654,30 +1611,22 @@
 		 * merge-recursive interoperate anyway, so punting for
 		 * now...
 		 */
-		remove_file(o, 0, collide_path, 0);
+		remove_file(opt, 0, collide_path, 0);
 	}
 
 	/* Store things in diff_filespecs for functions that need it */
-	memset(&a, 0, sizeof(struct diff_filespec));
-	memset(&b, 0, sizeof(struct diff_filespec));
-	null.path = a.path = b.path = (char *)collide_path;
+	null.path = (char *)collide_path;
 	oidcpy(&null.oid, &null_oid);
 	null.mode = 0;
-	oidcpy(&a.oid, a_oid);
-	a.mode = a_mode;
-	a.oid_valid = 1;
-	oidcpy(&b.oid, b_oid);
-	b.mode = b_mode;
-	b.oid_valid = 1;
 
-	if (merge_mode_and_contents(o, &null, &a, &b, collide_path,
-				    branch1, branch2, o->call_depth * 2, &mfi))
+	if (merge_mode_and_contents(opt, &null, a, b, collide_path,
+				    branch1, branch2, opt->call_depth * 2, &mfi))
 		return -1;
 	mfi.clean &= !alt_path;
-	if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path))
+	if (update_file(opt, mfi.clean, &mfi.blob, update_path))
 		return -1;
-	if (!mfi.clean && !o->call_depth &&
-	    update_stages(o, collide_path, NULL, &a, &b))
+	if (!mfi.clean && !opt->call_depth &&
+	    update_stages(opt, collide_path, NULL, a, b))
 		return -1;
 	free(alt_path);
 	/*
@@ -1690,52 +1639,57 @@
 	return mfi.clean;
 }
 
-static int handle_rename_add(struct merge_options *o,
+static int handle_rename_add(struct merge_options *opt,
 			     struct rename_conflict_info *ci)
 {
 	/* a was renamed to c, and a separate c was added. */
-	struct diff_filespec *a = ci->pair1->one;
-	struct diff_filespec *c = ci->pair1->two;
+	struct diff_filespec *a = ci->ren1->pair->one;
+	struct diff_filespec *c = ci->ren1->pair->two;
 	char *path = c->path;
 	char *prev_path_desc;
 	struct merge_file_info mfi;
 
-	int other_stage = (ci->branch1 == o->branch1 ? 3 : 2);
+	const char *rename_branch = ci->ren1->branch;
+	const char *add_branch = (opt->branch1 == rename_branch ?
+				  opt->branch2 : opt->branch1);
+	int other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2);
 
-	output(o, 1, _("CONFLICT (rename/add): "
+	output(opt, 1, _("CONFLICT (rename/add): "
 	       "Rename %s->%s in %s.  Added %s in %s"),
-	       a->path, c->path, ci->branch1,
-	       c->path, ci->branch2);
+	       a->path, c->path, rename_branch,
+	       c->path, add_branch);
 
 	prev_path_desc = xstrfmt("version of %s from %s", path, a->path);
-	if (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc,
-				    o->branch1, o->branch2,
-				    1 + o->call_depth * 2, &mfi))
+	if (merge_mode_and_contents(opt, a, c,
+				    &ci->ren1->src_entry->stages[other_stage],
+				    prev_path_desc,
+				    opt->branch1, opt->branch2,
+				    1 + opt->call_depth * 2, &mfi))
 		return -1;
 	free(prev_path_desc);
 
-	return handle_file_collision(o,
+	ci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path;
+	return handle_file_collision(opt,
 				     c->path, a->path, NULL,
-				     ci->branch1, ci->branch2,
-				     &mfi.oid, mfi.mode,
-				     &ci->dst_entry1->stages[other_stage].oid,
-				     ci->dst_entry1->stages[other_stage].mode);
+				     rename_branch, add_branch,
+				     &mfi.blob,
+				     &ci->ren1->dst_entry->stages[other_stage]);
 }
 
-static char *find_path_for_conflict(struct merge_options *o,
+static char *find_path_for_conflict(struct merge_options *opt,
 				    const char *path,
 				    const char *branch1,
 				    const char *branch2)
 {
 	char *new_path = NULL;
-	if (dir_in_way(o->repo->index, path, !o->call_depth, 0)) {
-		new_path = unique_path(o, path, branch1);
-		output(o, 1, _("%s is a directory in %s adding "
+	if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0)) {
+		new_path = unique_path(opt, path, branch1);
+		output(opt, 1, _("%s is a directory in %s adding "
 			       "as %s instead"),
 		       path, branch2, new_path);
-	} else if (would_lose_untracked(o, path)) {
-		new_path = unique_path(o, path, branch1);
-		output(o, 1, _("Refusing to lose untracked file"
+	} else if (would_lose_untracked(opt, path)) {
+		new_path = unique_path(opt, path, branch1);
+		output(opt, 1, _("Refusing to lose untracked file"
 			       " at %s; adding as %s instead"),
 		       path, new_path);
 	}
@@ -1743,41 +1697,40 @@
 	return new_path;
 }
 
-static int handle_rename_rename_1to2(struct merge_options *o,
+static int handle_rename_rename_1to2(struct merge_options *opt,
 				     struct rename_conflict_info *ci)
 {
 	/* One file was renamed in both branches, but to different names. */
 	struct merge_file_info mfi;
-	struct diff_filespec other;
 	struct diff_filespec *add;
-	struct diff_filespec *one = ci->pair1->one;
-	struct diff_filespec *a = ci->pair1->two;
-	struct diff_filespec *b = ci->pair2->two;
+	struct diff_filespec *o = ci->ren1->pair->one;
+	struct diff_filespec *a = ci->ren1->pair->two;
+	struct diff_filespec *b = ci->ren2->pair->two;
 	char *path_desc;
 
-	output(o, 1, _("CONFLICT (rename/rename): "
+	output(opt, 1, _("CONFLICT (rename/rename): "
 	       "Rename \"%s\"->\"%s\" in branch \"%s\" "
 	       "rename \"%s\"->\"%s\" in \"%s\"%s"),
-	       one->path, a->path, ci->branch1,
-	       one->path, b->path, ci->branch2,
-	       o->call_depth ? _(" (left unresolved)") : "");
+	       o->path, a->path, ci->ren1->branch,
+	       o->path, b->path, ci->ren2->branch,
+	       opt->call_depth ? _(" (left unresolved)") : "");
 
 	path_desc = xstrfmt("%s and %s, both renamed from %s",
-			    a->path, b->path, one->path);
-	if (merge_mode_and_contents(o, one, a, b, path_desc,
-				    ci->branch1, ci->branch2,
-				    o->call_depth * 2, &mfi))
+			    a->path, b->path, o->path);
+	if (merge_mode_and_contents(opt, o, a, b, path_desc,
+				    ci->ren1->branch, ci->ren2->branch,
+				    opt->call_depth * 2, &mfi))
 		return -1;
 	free(path_desc);
 
-	if (o->call_depth) {
+	if (opt->call_depth) {
 		/*
 		 * FIXME: For rename/add-source conflicts (if we could detect
 		 * such), this is wrong.  We should instead find a unique
 		 * pathname and then either rename the add-source file to that
 		 * unique path, or use that unique path instead of src here.
 		 */
-		if (update_file(o, 0, &mfi.oid, mfi.mode, one->path))
+		if (update_file(opt, 0, &mfi.blob, o->path))
 			return -1;
 
 		/*
@@ -1788,61 +1741,65 @@
 		 * such cases, we should keep the added file around,
 		 * resolving the conflict at that path in its favor.
 		 */
-		add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
-		if (add) {
-			if (update_file(o, 0, &add->oid, add->mode, a->path))
+		add = &ci->ren1->dst_entry->stages[2 ^ 1];
+		if (is_valid(add)) {
+			if (update_file(opt, 0, add, a->path))
 				return -1;
 		}
 		else
-			remove_file_from_index(o->repo->index, a->path);
-		add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
-		if (add) {
-			if (update_file(o, 0, &add->oid, add->mode, b->path))
+			remove_file_from_index(opt->repo->index, a->path);
+		add = &ci->ren2->dst_entry->stages[3 ^ 1];
+		if (is_valid(add)) {
+			if (update_file(opt, 0, add, b->path))
 				return -1;
 		}
 		else
-			remove_file_from_index(o->repo->index, b->path);
+			remove_file_from_index(opt->repo->index, b->path);
 	} else {
 		/*
 		 * For each destination path, we need to see if there is a
 		 * rename/add collision.  If not, we can write the file out
 		 * to the specified location.
 		 */
-		add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
-		if (add) {
-			if (handle_file_collision(o, a->path,
+		add = &ci->ren1->dst_entry->stages[2 ^ 1];
+		if (is_valid(add)) {
+			add->path = mfi.blob.path = a->path;
+			if (handle_file_collision(opt, a->path,
 						  NULL, NULL,
-						  ci->branch1, ci->branch2,
-						  &mfi.oid, mfi.mode,
-						  &add->oid, add->mode) < 0)
+						  ci->ren1->branch,
+						  ci->ren2->branch,
+						  &mfi.blob, add) < 0)
 				return -1;
 		} else {
-			char *new_path = find_path_for_conflict(o, a->path,
-								ci->branch1,
-								ci->branch2);
-			if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path))
+			char *new_path = find_path_for_conflict(opt, a->path,
+								ci->ren1->branch,
+								ci->ren2->branch);
+			if (update_file(opt, 0, &mfi.blob,
+					new_path ? new_path : a->path))
 				return -1;
 			free(new_path);
-			if (update_stages(o, a->path, NULL, a, NULL))
+			if (update_stages(opt, a->path, NULL, a, NULL))
 				return -1;
 		}
 
-		add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
-		if (add) {
-			if (handle_file_collision(o, b->path,
+		add = &ci->ren2->dst_entry->stages[3 ^ 1];
+		if (is_valid(add)) {
+			add->path = mfi.blob.path = b->path;
+			if (handle_file_collision(opt, b->path,
 						  NULL, NULL,
-						  ci->branch1, ci->branch2,
-						  &add->oid, add->mode,
-						  &mfi.oid, mfi.mode) < 0)
+						  ci->ren1->branch,
+						  ci->ren2->branch,
+						  add, &mfi.blob) < 0)
 				return -1;
 		} else {
-			char *new_path = find_path_for_conflict(o, b->path,
-								ci->branch2,
-								ci->branch1);
-			if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path))
+			char *new_path = find_path_for_conflict(opt, b->path,
+								ci->ren2->branch,
+								ci->ren1->branch);
+			if (update_file(opt, 0, &mfi.blob,
+					new_path ? new_path : b->path))
 				return -1;
 			free(new_path);
-			if (update_stages(o, b->path, NULL, NULL, b))
+			if (update_stages(opt, b->path, NULL, NULL, b))
 				return -1;
 		}
 	}
@@ -1850,58 +1807,68 @@
 	return 0;
 }
 
-static int handle_rename_rename_2to1(struct merge_options *o,
+static int handle_rename_rename_2to1(struct merge_options *opt,
 				     struct rename_conflict_info *ci)
 {
 	/* Two files, a & b, were renamed to the same thing, c. */
-	struct diff_filespec *a = ci->pair1->one;
-	struct diff_filespec *b = ci->pair2->one;
-	struct diff_filespec *c1 = ci->pair1->two;
-	struct diff_filespec *c2 = ci->pair2->two;
+	struct diff_filespec *a = ci->ren1->pair->one;
+	struct diff_filespec *b = ci->ren2->pair->one;
+	struct diff_filespec *c1 = ci->ren1->pair->two;
+	struct diff_filespec *c2 = ci->ren2->pair->two;
 	char *path = c1->path; /* == c2->path */
 	char *path_side_1_desc;
 	char *path_side_2_desc;
 	struct merge_file_info mfi_c1;
 	struct merge_file_info mfi_c2;
+	int ostage1, ostage2;
 
-	output(o, 1, _("CONFLICT (rename/rename): "
+	output(opt, 1, _("CONFLICT (rename/rename): "
 	       "Rename %s->%s in %s. "
 	       "Rename %s->%s in %s"),
-	       a->path, c1->path, ci->branch1,
-	       b->path, c2->path, ci->branch2);
+	       a->path, c1->path, ci->ren1->branch,
+	       b->path, c2->path, ci->ren2->branch);
 
 	path_side_1_desc = xstrfmt("version of %s from %s", path, a->path);
 	path_side_2_desc = xstrfmt("version of %s from %s", path, b->path);
-	if (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc,
-				    o->branch1, o->branch2,
-				    1 + o->call_depth * 2, &mfi_c1) ||
-	    merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,
-				    o->branch1, o->branch2,
-				    1 + o->call_depth * 2, &mfi_c2))
+	ostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2;
+	ostage2 = ostage1 ^ 1;
+	ci->ren1->src_entry->stages[ostage1].path = a->path;
+	ci->ren2->src_entry->stages[ostage2].path = b->path;
+	if (merge_mode_and_contents(opt, a, c1,
+				    &ci->ren1->src_entry->stages[ostage1],
+				    path_side_1_desc,
+				    opt->branch1, opt->branch2,
+				    1 + opt->call_depth * 2, &mfi_c1) ||
+	    merge_mode_and_contents(opt, b,
+				    &ci->ren2->src_entry->stages[ostage2],
+				    c2, path_side_2_desc,
+				    opt->branch1, opt->branch2,
+				    1 + opt->call_depth * 2, &mfi_c2))
 		return -1;
 	free(path_side_1_desc);
 	free(path_side_2_desc);
+	mfi_c1.blob.path = path;
+	mfi_c2.blob.path = path;
 
-	return handle_file_collision(o, path, a->path, b->path,
-				     ci->branch1, ci->branch2,
-				     &mfi_c1.oid, mfi_c1.mode,
-				     &mfi_c2.oid, mfi_c2.mode);
+	return handle_file_collision(opt, path, a->path, b->path,
+				     ci->ren1->branch, ci->ren2->branch,
+				     &mfi_c1.blob, &mfi_c2.blob);
 }
 
 /*
  * Get the diff_filepairs changed between o_tree and tree.
  */
-static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
+static struct diff_queue_struct *get_diffpairs(struct merge_options *opt,
 					       struct tree *o_tree,
 					       struct tree *tree)
 {
 	struct diff_queue_struct *ret;
 	struct diff_options opts;
 
-	repo_diff_setup(o->repo, &opts);
+	repo_diff_setup(opt->repo, &opts);
 	opts.flags.recursive = 1;
 	opts.flags.rename_empty = 0;
-	opts.detect_rename = merge_detect_rename(o);
+	opts.detect_rename = merge_detect_rename(opt);
 	/*
 	 * We do not have logic to handle the detection of copies.  In
 	 * fact, it may not even make sense to add such logic: would we
@@ -1910,17 +1877,17 @@
 	 */
 	if (opts.detect_rename > DIFF_DETECT_RENAME)
 		opts.detect_rename = DIFF_DETECT_RENAME;
-	opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
-			    o->diff_rename_limit >= 0 ? o->diff_rename_limit :
+	opts.rename_limit = opt->merge_rename_limit >= 0 ? opt->merge_rename_limit :
+			    opt->diff_rename_limit >= 0 ? opt->diff_rename_limit :
 			    1000;
-	opts.rename_score = o->rename_score;
-	opts.show_rename_progress = o->show_rename_progress;
+	opts.rename_score = opt->rename_score;
+	opts.show_rename_progress = opt->show_rename_progress;
 	opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 	diff_setup_done(&opts);
 	diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
 	diffcore_std(&opts);
-	if (opts.needed_rename_limit > o->needed_rename_limit)
-		o->needed_rename_limit = opts.needed_rename_limit;
+	if (opts.needed_rename_limit > opt->needed_rename_limit)
+		opt->needed_rename_limit = opts.needed_rename_limit;
 
 	ret = xmalloc(sizeof(*ret));
 	*ret = diff_queued_diff;
@@ -1935,7 +1902,7 @@
 static int tree_has_path(struct tree *tree, const char *path)
 {
 	struct object_id hashy;
-	unsigned int mode_o;
+	unsigned short mode_o;
 
 	return !get_tree_entry(&tree->object.oid, path,
 			       &hashy, &mode_o);
@@ -2039,7 +2006,7 @@
  * level conflicts for the renamed location.  If there is a rename and
  * there are no conflicts, return the new name.  Otherwise, return NULL.
  */
-static char *handle_path_level_conflicts(struct merge_options *o,
+static char *handle_path_level_conflicts(struct merge_options *opt,
 					 const char *path,
 					 struct dir_rename_entry *entry,
 					 struct hashmap *collisions,
@@ -2060,7 +2027,7 @@
 		/* This should only happen when entry->non_unique_new_dir set */
 		if (!entry->non_unique_new_dir)
 			BUG("entry->non_unqiue_dir not set and !new_path");
-		output(o, 1, _("CONFLICT (directory rename split): "
+		output(opt, 1, _("CONFLICT (directory rename split): "
 			       "Unclear where to place %s because directory "
 			       "%s was renamed to multiple other directories, "
 			       "with no destination getting a majority of the "
@@ -2092,7 +2059,7 @@
 		collision_ent->reported_already = 1;
 		strbuf_add_separated_string_list(&collision_paths, ", ",
 						 &collision_ent->source_files);
-		output(o, 1, _("CONFLICT (implicit dir rename): Existing "
+		output(opt, 1, _("CONFLICT (implicit dir rename): Existing "
 			       "file/dir at %s in the way of implicit "
 			       "directory rename(s) putting the following "
 			       "path(s) there: %s."),
@@ -2102,7 +2069,7 @@
 		collision_ent->reported_already = 1;
 		strbuf_add_separated_string_list(&collision_paths, ", ",
 						 &collision_ent->source_files);
-		output(o, 1, _("CONFLICT (implicit dir rename): Cannot map "
+		output(opt, 1, _("CONFLICT (implicit dir rename): Cannot map "
 			       "more than one path to %s; implicit directory "
 			       "renames tried to put these paths there: %s"),
 		       new_path, collision_paths.buf);
@@ -2139,7 +2106,7 @@
  *         causes conflicts for files within those merged directories, then
  *         that should be detected at the individual path level.
  */
-static void handle_directory_level_conflicts(struct merge_options *o,
+static void handle_directory_level_conflicts(struct merge_options *opt,
 					     struct hashmap *dir_re_head,
 					     struct tree *head,
 					     struct hashmap *dir_re_merge,
@@ -2194,11 +2161,11 @@
 			 * know that head_ent->new_dir and merge_ent->new_dir
 			 * are different strings.
 			 */
-			output(o, 1, _("CONFLICT (rename/rename): "
+			output(opt, 1, _("CONFLICT (rename/rename): "
 				       "Rename directory %s->%s in %s. "
 				       "Rename directory %s->%s in %s"),
-			       head_ent->dir, head_ent->new_dir.buf, o->branch1,
-			       head_ent->dir, merge_ent->new_dir.buf, o->branch2);
+			       head_ent->dir, head_ent->new_dir.buf, opt->branch1,
+			       head_ent->dir, merge_ent->new_dir.buf, opt->branch2);
 			string_list_append(&remove_from_head,
 					   head_ent->dir)->util = head_ent;
 			strbuf_release(&head_ent->new_dir);
@@ -2397,7 +2364,7 @@
 	}
 }
 
-static char *check_for_directory_rename(struct merge_options *o,
+static char *check_for_directory_rename(struct merge_options *opt,
 					const char *path,
 					struct tree *tree,
 					struct hashmap *dir_renames,
@@ -2438,11 +2405,11 @@
 	 */
 	oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);
 	if (oentry) {
-		output(o, 1, _("WARNING: Avoiding applying %s -> %s rename "
+		output(opt, 1, _("WARNING: Avoiding applying %s -> %s rename "
 			       "to %s, because %s itself was renamed."),
 		       entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);
 	} else {
-		new_path = handle_path_level_conflicts(o, path, entry,
+		new_path = handle_path_level_conflicts(opt, path, entry,
 						       collisions, tree);
 		*clean_merge &= (new_path != NULL);
 	}
@@ -2450,7 +2417,7 @@
 	return new_path;
 }
 
-static void apply_directory_rename_modifications(struct merge_options *o,
+static void apply_directory_rename_modifications(struct merge_options *opt,
 						 struct diff_filepair *pair,
 						 char *new_path,
 						 struct rename *re,
@@ -2473,11 +2440,11 @@
 	 * saying the file would have been overwritten), but it might
 	 * be dirty, though.
 	 */
-	update_wd = !was_dirty(o, pair->two->path);
+	update_wd = !was_dirty(opt, pair->two->path);
 	if (!update_wd)
-		output(o, 1, _("Refusing to lose dirty file at %s"),
+		output(opt, 1, _("Refusing to lose dirty file at %s"),
 		       pair->two->path);
-	remove_file(o, 1, pair->two->path, !update_wd);
+	remove_file(opt, 1, pair->two->path, !update_wd);
 
 	/* Find or create a new re->dst_entry */
 	item = string_list_lookup(entries, new_path);
@@ -2537,16 +2504,18 @@
 		       &re->dst_entry->stages[stage].oid,
 		       &re->dst_entry->stages[stage].mode);
 
-	/* Update pair status */
-	if (pair->status == 'A') {
-		/*
-		 * Recording rename information for this add makes it look
-		 * like a rename/delete conflict.  Make sure we can
-		 * correctly handle this as an add that was moved to a new
-		 * directory instead of reporting a rename/delete conflict.
-		 */
-		re->add_turned_into_rename = 1;
-	}
+	/*
+	 * Record the original change status (or 'type' of change).  If it
+	 * was originally an add ('A'), this lets us differentiate later
+	 * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they
+	 * otherwise look the same).  If it was originally a rename ('R'),
+	 * this lets us remember and report accurately about the transitive
+	 * renaming that occurred via the directory rename detection.  Also,
+	 * record the original destination name.
+	 */
+	re->dir_rename_original_type = pair->status;
+	re->dir_rename_original_dest = pair->two->path;
+
 	/*
 	 * We don't actually look at pair->status again, but it seems
 	 * pedagogically correct to adjust it.
@@ -2566,7 +2535,8 @@
  * to be able to associate the correct cache entries with the rename
  * information; tree is always equal to either a_tree or b_tree.
  */
-static struct string_list *get_renames(struct merge_options *o,
+static struct string_list *get_renames(struct merge_options *opt,
+				       const char *branch,
 				       struct diff_queue_struct *pairs,
 				       struct hashmap *dir_renames,
 				       struct hashmap *dir_rename_exclusions,
@@ -2596,7 +2566,7 @@
 			diff_free_filepair(pair);
 			continue;
 		}
-		new_path = check_for_directory_rename(o, pair->two->path, tree,
+		new_path = check_for_directory_rename(opt, pair->two->path, tree,
 						      dir_renames,
 						      dir_rename_exclusions,
 						      &collisions,
@@ -2608,8 +2578,10 @@
 
 		re = xmalloc(sizeof(*re));
 		re->processed = 0;
-		re->add_turned_into_rename = 0;
 		re->pair = pair;
+		re->branch = branch;
+		re->dir_rename_original_type = '\0';
+		re->dir_rename_original_dest = NULL;
 		item = string_list_lookup(entries, re->pair->one->path);
 		if (!item)
 			re->src_entry = insert_stage_data(re->pair->one->path,
@@ -2626,7 +2598,7 @@
 		item = string_list_insert(renames, pair->one->path);
 		item->util = re;
 		if (new_path)
-			apply_directory_rename_modifications(o, pair, new_path,
+			apply_directory_rename_modifications(opt, pair, new_path,
 							     re, tree, o_tree,
 							     a_tree, b_tree,
 							     entries);
@@ -2641,7 +2613,7 @@
 	return renames;
 }
 
-static int process_renames(struct merge_options *o,
+static int process_renames(struct merge_options *opt,
 			   struct string_list *a_renames,
 			   struct string_list *b_renames)
 {
@@ -2664,7 +2636,6 @@
 	for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
 		struct string_list *renames1, *renames2Dst;
 		struct rename *ren1 = NULL, *ren2 = NULL;
-		const char *branch1, *branch2;
 		const char *ren1_src, *ren1_dst;
 		struct string_list_item *lookup;
 
@@ -2685,13 +2656,9 @@
 		if (ren1) {
 			renames1 = a_renames;
 			renames2Dst = &b_by_dst;
-			branch1 = o->branch1;
-			branch2 = o->branch2;
 		} else {
 			renames1 = b_renames;
 			renames2Dst = &a_by_dst;
-			branch1 = o->branch2;
-			branch2 = o->branch1;
 			SWAP(ren2, ren1);
 		}
 
@@ -2725,22 +2692,13 @@
 				 * the base stage (think of rename +
 				 * add-source cases).
 				 */
-				remove_file(o, 1, ren1_src, 1);
+				remove_file(opt, 1, ren1_src, 1);
 				update_entry(ren1->dst_entry,
 					     ren1->pair->one,
 					     ren1->pair->two,
 					     ren2->pair->two);
 			}
-			setup_rename_conflict_info(rename_type,
-						   ren1->pair,
-						   ren2->pair,
-						   branch1,
-						   branch2,
-						   ren1->dst_entry,
-						   ren2->dst_entry,
-						   o,
-						   NULL,
-						   NULL);
+			setup_rename_conflict_info(rename_type, opt, ren1, ren2);
 		} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {
 			/* Two different files renamed to the same thing */
 			char *ren2_dst;
@@ -2759,16 +2717,7 @@
 			ren2->src_entry->processed = 1;
 
 			setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,
-						   ren1->pair,
-						   ren2->pair,
-						   branch1,
-						   branch2,
-						   ren1->dst_entry,
-						   ren2->dst_entry,
-						   o,
-						   ren1->src_entry,
-						   ren2->src_entry);
-
+						   opt, ren1, ren2);
 		} else {
 			/* Renamed in 1, maybe changed in 2 */
 			/* we only use sha1 and mode of these */
@@ -2788,8 +2737,8 @@
 			 * stage and in other_stage (think of rename +
 			 * add-source case).
 			 */
-			remove_file(o, 1, ren1_src,
-				    renamed_stage == 2 || !was_tracked(o, ren1_src));
+			remove_file(opt, 1, ren1_src,
+				    renamed_stage == 2 || !was_tracked(opt, ren1_src));
 
 			oidcpy(&src_other.oid,
 			       &ren1->src_entry->stages[other_stage].oid);
@@ -2800,28 +2749,12 @@
 			try_merge = 0;
 
 			if (oid_eq(&src_other.oid, &null_oid) &&
-			    ren1->add_turned_into_rename) {
+			    ren1->dir_rename_original_type == 'A') {
 				setup_rename_conflict_info(RENAME_VIA_DIR,
-							   ren1->pair,
-							   NULL,
-							   branch1,
-							   branch2,
-							   ren1->dst_entry,
-							   NULL,
-							   o,
-							   NULL,
-							   NULL);
+							   opt, ren1, NULL);
 			} else if (oid_eq(&src_other.oid, &null_oid)) {
 				setup_rename_conflict_info(RENAME_DELETE,
-							   ren1->pair,
-							   NULL,
-							   branch1,
-							   branch2,
-							   ren1->dst_entry,
-							   NULL,
-							   o,
-							   NULL,
-							   NULL);
+							   opt, ren1, NULL);
 			} else if ((dst_other.mode == ren1->pair->two->mode) &&
 				   oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {
 				/*
@@ -2832,9 +2765,8 @@
 				 * update_file_flags() instead of
 				 * update_file().
 				 */
-				if (update_file_flags(o,
-						      &ren1->pair->two->oid,
-						      ren1->pair->two->mode,
+				if (update_file_flags(opt,
+						      ren1->pair->two,
 						      ren1_dst,
 						      1, /* update_cache */
 						      0  /* update_wd    */))
@@ -2848,25 +2780,17 @@
 				 * file, then the merge will be clean.
 				 */
 				setup_rename_conflict_info(RENAME_ADD,
-							   ren1->pair,
-							   NULL,
-							   branch1,
-							   branch2,
-							   ren1->dst_entry,
-							   NULL,
-							   o,
-							   ren1->src_entry,
-							   NULL);
+							   opt, ren1, NULL);
 			} else
 				try_merge = 1;
 
 			if (clean_merge < 0)
 				goto cleanup_and_return;
 			if (try_merge) {
-				struct diff_filespec *one, *a, *b;
+				struct diff_filespec *o, *a, *b;
 				src_other.path = (char *)ren1_src;
 
-				one = ren1->pair->one;
+				o = ren1->pair->one;
 				if (a_renames == renames1) {
 					a = ren1->pair->two;
 					b = &src_other;
@@ -2874,17 +2798,9 @@
 					b = ren1->pair->two;
 					a = &src_other;
 				}
-				update_entry(ren1->dst_entry, one, a, b);
+				update_entry(ren1->dst_entry, o, a, b);
 				setup_rename_conflict_info(RENAME_NORMAL,
-							   ren1->pair,
-							   NULL,
-							   branch1,
-							   NULL,
-							   ren1->dst_entry,
-							   NULL,
-							   o,
-							   NULL,
-							   NULL);
+							   opt, ren1, NULL);
 			}
 		}
 	}
@@ -2919,7 +2835,7 @@
 	free(pairs);
 }
 
-static int detect_and_process_renames(struct merge_options *o,
+static int detect_and_process_renames(struct merge_options *opt,
 				      struct tree *common,
 				      struct tree *head,
 				      struct tree *merge,
@@ -2933,17 +2849,17 @@
 	ri->head_renames = NULL;
 	ri->merge_renames = NULL;
 
-	if (!merge_detect_rename(o))
+	if (!merge_detect_rename(opt))
 		return 1;
 
-	head_pairs = get_diffpairs(o, common, head);
-	merge_pairs = get_diffpairs(o, common, merge);
+	head_pairs = get_diffpairs(opt, common, head);
+	merge_pairs = get_diffpairs(opt, common, merge);
 
-	if (o->detect_directory_renames) {
+	if (opt->detect_directory_renames) {
 		dir_re_head = get_directory_renames(head_pairs);
 		dir_re_merge = get_directory_renames(merge_pairs);
 
-		handle_directory_level_conflicts(o,
+		handle_directory_level_conflicts(opt,
 						 dir_re_head, head,
 						 dir_re_merge, merge);
 	} else {
@@ -2953,19 +2869,19 @@
 		dir_rename_init(dir_re_merge);
 	}
 
-	ri->head_renames  = get_renames(o, head_pairs,
+	ri->head_renames  = get_renames(opt, opt->branch1, head_pairs,
 					dir_re_merge, dir_re_head, head,
 					common, head, merge, entries,
 					&clean);
 	if (clean < 0)
 		goto cleanup;
-	ri->merge_renames = get_renames(o, merge_pairs,
+	ri->merge_renames = get_renames(opt, opt->branch2, merge_pairs,
 					dir_re_head, dir_re_merge, merge,
 					common, head, merge, entries,
 					&clean);
 	if (clean < 0)
 		goto cleanup;
-	clean &= process_renames(o, ri->head_renames, ri->merge_renames);
+	clean &= process_renames(opt, ri->head_renames, ri->merge_renames);
 
 cleanup:
 	/*
@@ -3001,12 +2917,7 @@
 	final_cleanup_rename(re_info->merge_renames);
 }
 
-static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
-{
-	return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
-}
-
-static int read_oid_strbuf(struct merge_options *o,
+static int read_oid_strbuf(struct merge_options *opt,
 			   const struct object_id *oid,
 			   struct strbuf *dst)
 {
@@ -3015,130 +2926,99 @@
 	unsigned long size;
 	buf = read_object_file(oid, &type, &size);
 	if (!buf)
-		return err(o, _("cannot read object %s"), oid_to_hex(oid));
+		return err(opt, _("cannot read object %s"), oid_to_hex(oid));
 	if (type != OBJ_BLOB) {
 		free(buf);
-		return err(o, _("object %s is not a blob"), oid_to_hex(oid));
+		return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
 	}
 	strbuf_attach(dst, buf, size, size + 1);
 	return 0;
 }
 
 static int blob_unchanged(struct merge_options *opt,
-			  const struct object_id *o_oid,
-			  unsigned o_mode,
-			  const struct object_id *a_oid,
-			  unsigned a_mode,
+			  const struct diff_filespec *o,
+			  const struct diff_filespec *a,
 			  int renormalize, const char *path)
 {
-	struct strbuf o = STRBUF_INIT;
-	struct strbuf a = STRBUF_INIT;
+	struct strbuf obuf = STRBUF_INIT;
+	struct strbuf abuf = STRBUF_INIT;
 	int ret = 0; /* assume changed for safety */
+	const struct index_state *idx = opt->repo->index;
 
-	if (a_mode != o_mode)
+	if (a->mode != o->mode)
 		return 0;
-	if (oid_eq(o_oid, a_oid))
+	if (oid_eq(&o->oid, &a->oid))
 		return 1;
 	if (!renormalize)
 		return 0;
 
-	assert(o_oid && a_oid);
-	if (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))
+	if (read_oid_strbuf(opt, &o->oid, &obuf) ||
+	    read_oid_strbuf(opt, &a->oid, &abuf))
 		goto error_return;
 	/*
 	 * Note: binary | is used so that both renormalizations are
 	 * performed.  Comparison can be skipped if both files are
 	 * unchanged since their sha1s have already been compared.
 	 */
-	if (renormalize_buffer(opt->repo->index, path, o.buf, o.len, &o) |
-	    renormalize_buffer(opt->repo->index, path, a.buf, a.len, &a))
-		ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+	if (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) |
+	    renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf))
+		ret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len));
 
 error_return:
-	strbuf_release(&o);
-	strbuf_release(&a);
+	strbuf_release(&obuf);
+	strbuf_release(&abuf);
 	return ret;
 }
 
-static int handle_modify_delete(struct merge_options *o,
+static int handle_modify_delete(struct merge_options *opt,
 				const char *path,
-				struct object_id *o_oid, int o_mode,
-				struct object_id *a_oid, int a_mode,
-				struct object_id *b_oid, int b_mode)
+				const struct diff_filespec *o,
+				const struct diff_filespec *a,
+				const struct diff_filespec *b)
 {
 	const char *modify_branch, *delete_branch;
-	struct object_id *changed_oid;
-	int changed_mode;
+	const struct diff_filespec *changed;
 
-	if (a_oid) {
-		modify_branch = o->branch1;
-		delete_branch = o->branch2;
-		changed_oid = a_oid;
-		changed_mode = a_mode;
+	if (is_valid(a)) {
+		modify_branch = opt->branch1;
+		delete_branch = opt->branch2;
+		changed = a;
 	} else {
-		modify_branch = o->branch2;
-		delete_branch = o->branch1;
-		changed_oid = b_oid;
-		changed_mode = b_mode;
+		modify_branch = opt->branch2;
+		delete_branch = opt->branch1;
+		changed = b;
 	}
 
-	return handle_change_delete(o,
+	return handle_change_delete(opt,
 				    path, NULL,
-				    o_oid, o_mode,
-				    changed_oid, changed_mode,
+				    o, changed,
 				    modify_branch, delete_branch,
 				    _("modify"), _("modified"));
 }
 
-static int handle_content_merge(struct merge_options *o,
+static int handle_content_merge(struct merge_file_info *mfi,
+				struct merge_options *opt,
 				const char *path,
 				int is_dirty,
-				struct object_id *o_oid, int o_mode,
-				struct object_id *a_oid, int a_mode,
-				struct object_id *b_oid, int b_mode,
-				struct rename_conflict_info *rename_conflict_info)
+				const struct diff_filespec *o,
+				const struct diff_filespec *a,
+				const struct diff_filespec *b,
+				struct rename_conflict_info *ci)
 {
 	const char *reason = _("content");
-	const char *path1 = NULL, *path2 = NULL;
-	struct merge_file_info mfi;
-	struct diff_filespec one, a, b;
 	unsigned df_conflict_remains = 0;
 
-	if (!o_oid) {
+	if (!is_valid(o))
 		reason = _("add/add");
-		o_oid = (struct object_id *)&null_oid;
-	}
-	one.path = a.path = b.path = (char *)path;
-	oidcpy(&one.oid, o_oid);
-	one.mode = o_mode;
-	oidcpy(&a.oid, a_oid);
-	a.mode = a_mode;
-	oidcpy(&b.oid, b_oid);
-	b.mode = b_mode;
 
-	if (rename_conflict_info) {
-		struct diff_filepair *pair1 = rename_conflict_info->pair1;
+	assert(o->path && a->path && b->path);
+	if (ci && dir_in_way(opt->repo->index, path, !opt->call_depth,
+			     S_ISGITLINK(ci->ren1->pair->two->mode)))
+		df_conflict_remains = 1;
 
-		path1 = (o->branch1 == rename_conflict_info->branch1) ?
-			pair1->two->path : pair1->one->path;
-		/* If rename_conflict_info->pair2 != NULL, we are in
-		 * RENAME_ONE_FILE_TO_ONE case.  Otherwise, we have a
-		 * normal rename.
-		 */
-		path2 = (rename_conflict_info->pair2 ||
-			 o->branch2 == rename_conflict_info->branch1) ?
-			pair1->two->path : pair1->one->path;
-		one.path = pair1->one->path;
-		a.path = (char *)path1;
-		b.path = (char *)path2;
-
-		if (dir_in_way(o->repo->index, path, !o->call_depth,
-			       S_ISGITLINK(pair1->two->mode)))
-			df_conflict_remains = 1;
-	}
-	if (merge_mode_and_contents(o, &one, &a, &b, path,
-				    o->branch1, o->branch2,
-				    o->call_depth * 2, &mfi))
+	if (merge_mode_and_contents(opt, o, a, b, path,
+				    opt->branch1, opt->branch2,
+				    opt->call_depth * 2, mfi))
 		return -1;
 
 	/*
@@ -3147,15 +3027,14 @@
 	 *   b) The merge matches what was in HEAD (content, mode, pathname)
 	 *   c) The target path is usable (i.e. not involved in D/F conflict)
 	 */
-	if (mfi.clean &&
-	    was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&
+	if (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) &&
 	    !df_conflict_remains) {
 		int pos;
 		struct cache_entry *ce;
 
-		output(o, 3, _("Skipped %s (merged same as existing)"), path);
-		if (add_cacheinfo(o, mfi.mode, &mfi.oid, path,
-				  0, (!o->call_depth && !is_dirty), 0))
+		output(opt, 3, _("Skipped %s (merged same as existing)"), path);
+		if (add_cacheinfo(opt, &mfi->blob, path,
+				  0, (!opt->call_depth && !is_dirty), 0))
 			return -1;
 		/*
 		 * However, add_cacheinfo() will delete the old cache entry
@@ -3163,109 +3042,196 @@
 		 * flag to avoid making the file appear as if it were
 		 * deleted by the user.
 		 */
-		pos = index_name_pos(&o->orig_index, path, strlen(path));
-		ce = o->orig_index.cache[pos];
+		pos = index_name_pos(&opt->orig_index, path, strlen(path));
+		ce = opt->orig_index.cache[pos];
 		if (ce_skip_worktree(ce)) {
-			pos = index_name_pos(o->repo->index, path, strlen(path));
-			ce = o->repo->index->cache[pos];
+			pos = index_name_pos(opt->repo->index, path, strlen(path));
+			ce = opt->repo->index->cache[pos];
 			ce->ce_flags |= CE_SKIP_WORKTREE;
 		}
-		return mfi.clean;
+		return mfi->clean;
 	}
 
-	if (!mfi.clean) {
-		if (S_ISGITLINK(mfi.mode))
+	if (!mfi->clean) {
+		if (S_ISGITLINK(mfi->blob.mode))
 			reason = _("submodule");
-		output(o, 1, _("CONFLICT (%s): Merge conflict in %s"),
+		output(opt, 1, _("CONFLICT (%s): Merge conflict in %s"),
 				reason, path);
-		if (rename_conflict_info && !df_conflict_remains)
-			if (update_stages(o, path, &one, &a, &b))
+		if (ci && !df_conflict_remains)
+			if (update_stages(opt, path, o, a, b))
 				return -1;
 	}
 
 	if (df_conflict_remains || is_dirty) {
 		char *new_path;
-		if (o->call_depth) {
-			remove_file_from_index(o->repo->index, path);
+		if (opt->call_depth) {
+			remove_file_from_index(opt->repo->index, path);
 		} else {
-			if (!mfi.clean) {
-				if (update_stages(o, path, &one, &a, &b))
+			if (!mfi->clean) {
+				if (update_stages(opt, path, o, a, b))
 					return -1;
 			} else {
-				int file_from_stage2 = was_tracked(o, path);
-				struct diff_filespec merged;
-				oidcpy(&merged.oid, &mfi.oid);
-				merged.mode = mfi.mode;
+				int file_from_stage2 = was_tracked(opt, path);
 
-				if (update_stages(o, path, NULL,
-						  file_from_stage2 ? &merged : NULL,
-						  file_from_stage2 ? NULL : &merged))
+				if (update_stages(opt, path, NULL,
+						  file_from_stage2 ? &mfi->blob : NULL,
+						  file_from_stage2 ? NULL : &mfi->blob))
 					return -1;
 			}
 
 		}
-		new_path = unique_path(o, path, rename_conflict_info->branch1);
+		new_path = unique_path(opt, path, ci->ren1->branch);
 		if (is_dirty) {
-			output(o, 1, _("Refusing to lose dirty file at %s"),
+			output(opt, 1, _("Refusing to lose dirty file at %s"),
 			       path);
 		}
-		output(o, 1, _("Adding as %s instead"), new_path);
-		if (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {
+		output(opt, 1, _("Adding as %s instead"), new_path);
+		if (update_file(opt, 0, &mfi->blob, new_path)) {
 			free(new_path);
 			return -1;
 		}
 		free(new_path);
-		mfi.clean = 0;
-	} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))
+		mfi->clean = 0;
+	} else if (update_file(opt, mfi->clean, &mfi->blob, path))
 		return -1;
-	return !is_dirty && mfi.clean;
+	return !is_dirty && mfi->clean;
 }
 
-static int handle_rename_normal(struct merge_options *o,
+static int handle_rename_normal(struct merge_options *opt,
 				const char *path,
-				struct object_id *o_oid, unsigned int o_mode,
-				struct object_id *a_oid, unsigned int a_mode,
-				struct object_id *b_oid, unsigned int b_mode,
+				const struct diff_filespec *o,
+				const struct diff_filespec *a,
+				const struct diff_filespec *b,
 				struct rename_conflict_info *ci)
 {
+	struct rename *ren = ci->ren1;
+	struct merge_file_info mfi;
+	int clean;
+	int side = (ren->branch == opt->branch1 ? 2 : 3);
+
 	/* Merge the content and write it out */
-	return handle_content_merge(o, path, was_dirty(o, path),
-				    o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
-				    ci);
+	clean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),
+				     o, a, b, ci);
+
+	if (clean && opt->detect_directory_renames == 1 &&
+	    ren->dir_rename_original_dest) {
+		if (update_stages(opt, path,
+				  NULL,
+				  side == 2 ? &mfi.blob : NULL,
+				  side == 2 ? NULL : &mfi.blob))
+			return -1;
+		clean = 0; /* not clean, but conflicted */
+	}
+	return clean;
+}
+
+static void dir_rename_warning(const char *msg,
+			       int is_add,
+			       int clean,
+			       struct merge_options *opt,
+			       struct rename *ren)
+{
+	const char *other_branch;
+	other_branch = (ren->branch == opt->branch1 ?
+			opt->branch2 : opt->branch1);
+	if (is_add) {
+		output(opt, clean ? 2 : 1, msg,
+		       ren->pair->one->path, ren->branch,
+		       other_branch, ren->pair->two->path);
+		return;
+	}
+	output(opt, clean ? 2 : 1, msg,
+	       ren->pair->one->path, ren->dir_rename_original_dest, ren->branch,
+	       other_branch, ren->pair->two->path);
+}
+static int warn_about_dir_renamed_entries(struct merge_options *opt,
+					  struct rename *ren)
+{
+	const char *msg;
+	int clean = 1, is_add;
+
+	if (!ren)
+		return clean;
+
+	/* Return early if ren was not affected/created by a directory rename */
+	if (!ren->dir_rename_original_dest)
+		return clean;
+
+	/* Sanity checks */
+	assert(opt->detect_directory_renames > 0);
+	assert(ren->dir_rename_original_type == 'A' ||
+	       ren->dir_rename_original_type == 'R');
+
+	/* Check whether to treat directory renames as a conflict */
+	clean = (opt->detect_directory_renames == 2);
+
+	is_add = (ren->dir_rename_original_type == 'A');
+	if (ren->dir_rename_original_type == 'A' && clean) {
+		msg = _("Path updated: %s added in %s inside a "
+			"directory that was renamed in %s; moving it to %s.");
+	} else if (ren->dir_rename_original_type == 'A' && !clean) {
+		msg = _("CONFLICT (file location): %s added in %s "
+			"inside a directory that was renamed in %s, "
+			"suggesting it should perhaps be moved to %s.");
+	} else if (ren->dir_rename_original_type == 'R' && clean) {
+		msg = _("Path updated: %s renamed to %s in %s, inside a "
+			"directory that was renamed in %s; moving it to %s.");
+	} else if (ren->dir_rename_original_type == 'R' && !clean) {
+		msg = _("CONFLICT (file location): %s renamed to %s in %s, "
+			"inside a directory that was renamed in %s, "
+			"suggesting it should perhaps be moved to %s.");
+	} else {
+		BUG("Impossible dir_rename_original_type/clean combination");
+	}
+	dir_rename_warning(msg, is_add, clean, opt, ren);
+
+	return clean;
 }
 
 /* Per entry merge function */
-static int process_entry(struct merge_options *o,
+static int process_entry(struct merge_options *opt,
 			 const char *path, struct stage_data *entry)
 {
 	int clean_merge = 1;
-	int normalize = o->renormalize;
-	unsigned o_mode = entry->stages[1].mode;
-	unsigned a_mode = entry->stages[2].mode;
-	unsigned b_mode = entry->stages[3].mode;
-	struct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode);
-	struct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode);
-	struct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode);
+	int normalize = opt->renormalize;
+
+	struct diff_filespec *o = &entry->stages[1];
+	struct diff_filespec *a = &entry->stages[2];
+	struct diff_filespec *b = &entry->stages[3];
+	int o_valid = is_valid(o);
+	int a_valid = is_valid(a);
+	int b_valid = is_valid(b);
+	o->path = a->path = b->path = (char*)path;
 
 	entry->processed = 1;
 	if (entry->rename_conflict_info) {
-		struct rename_conflict_info *conflict_info = entry->rename_conflict_info;
-		switch (conflict_info->rename_type) {
+		struct rename_conflict_info *ci = entry->rename_conflict_info;
+		struct diff_filespec *temp;
+		int path_clean;
+
+		path_clean = warn_about_dir_renamed_entries(opt, ci->ren1);
+		path_clean &= warn_about_dir_renamed_entries(opt, ci->ren2);
+
+		/*
+		 * For cases with a single rename, {o,a,b}->path have all been
+		 * set to the rename target path; we need to set two of these
+		 * back to the rename source.
+		 * For rename/rename conflicts, we'll manually fix paths below.
+		 */
+		temp = (opt->branch1 == ci->ren1->branch) ? b : a;
+		o->path = temp->path = ci->ren1->pair->one->path;
+		if (ci->ren2) {
+			assert(opt->branch1 == ci->ren1->branch);
+		}
+
+		switch (ci->rename_type) {
 		case RENAME_NORMAL:
 		case RENAME_ONE_FILE_TO_ONE:
-			clean_merge = handle_rename_normal(o,
-							   path,
-							   o_oid, o_mode,
-							   a_oid, a_mode,
-							   b_oid, b_mode,
-							   conflict_info);
+			clean_merge = handle_rename_normal(opt, path, o, a, b,
+							   ci);
 			break;
 		case RENAME_VIA_DIR:
-			clean_merge = 1;
-			if (handle_rename_via_dir(o,
-						  conflict_info->pair1,
-						  conflict_info->branch1))
-				clean_merge = -1;
+			clean_merge = handle_rename_via_dir(opt, ci);
 			break;
 		case RENAME_ADD:
 			/*
@@ -3274,165 +3240,172 @@
 			 * two-way merged cleanly with the added file, I
 			 * guess it's a clean merge?
 			 */
-			clean_merge = handle_rename_add(o, conflict_info);
+			clean_merge = handle_rename_add(opt, ci);
 			break;
 		case RENAME_DELETE:
 			clean_merge = 0;
-			if (handle_rename_delete(o,
-						 conflict_info->pair1,
-						 conflict_info->branch1,
-						 conflict_info->branch2))
+			if (handle_rename_delete(opt, ci))
 				clean_merge = -1;
 			break;
 		case RENAME_ONE_FILE_TO_TWO:
+			/*
+			 * Manually fix up paths; note:
+			 * ren[12]->pair->one->path are equal.
+			 */
+			o->path = ci->ren1->pair->one->path;
+			a->path = ci->ren1->pair->two->path;
+			b->path = ci->ren2->pair->two->path;
+
 			clean_merge = 0;
-			if (handle_rename_rename_1to2(o, conflict_info))
+			if (handle_rename_rename_1to2(opt, ci))
 				clean_merge = -1;
 			break;
 		case RENAME_TWO_FILES_TO_ONE:
 			/*
+			 * Manually fix up paths; note,
+			 * ren[12]->pair->two->path are actually equal.
+			 */
+			o->path = NULL;
+			a->path = ci->ren1->pair->two->path;
+			b->path = ci->ren2->pair->two->path;
+
+			/*
 			 * Probably unclean merge, but if the two renamed
 			 * files merge cleanly and the two resulting files
 			 * can then be two-way merged cleanly, I guess it's
 			 * a clean merge?
 			 */
-			clean_merge = handle_rename_rename_2to1(o,
-								conflict_info);
+			clean_merge = handle_rename_rename_2to1(opt, ci);
 			break;
 		default:
 			entry->processed = 0;
 			break;
 		}
-	} else if (o_oid && (!a_oid || !b_oid)) {
+		if (path_clean < clean_merge)
+			clean_merge = path_clean;
+	} else if (o_valid && (!a_valid || !b_valid)) {
 		/* Case A: Deleted in one */
-		if ((!a_oid && !b_oid) ||
-		    (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||
-		    (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {
+		if ((!a_valid && !b_valid) ||
+		    (!b_valid && blob_unchanged(opt, o, a, normalize, path)) ||
+		    (!a_valid && blob_unchanged(opt, o, b, normalize, path))) {
 			/* Deleted in both or deleted in one and
 			 * unchanged in the other */
-			if (a_oid)
-				output(o, 2, _("Removing %s"), path);
+			if (a_valid)
+				output(opt, 2, _("Removing %s"), path);
 			/* do not touch working file if it did not exist */
-			remove_file(o, 1, path, !a_oid);
+			remove_file(opt, 1, path, !a_valid);
 		} else {
 			/* Modify/delete; deleted side may have put a directory in the way */
 			clean_merge = 0;
-			if (handle_modify_delete(o, path, o_oid, o_mode,
-						 a_oid, a_mode, b_oid, b_mode))
+			if (handle_modify_delete(opt, path, o, a, b))
 				clean_merge = -1;
 		}
-	} else if ((!o_oid && a_oid && !b_oid) ||
-		   (!o_oid && !a_oid && b_oid)) {
+	} else if ((!o_valid && a_valid && !b_valid) ||
+		   (!o_valid && !a_valid && b_valid)) {
 		/* Case B: Added in one. */
 		/* [nothing|directory] -> ([nothing|directory], file) */
 
 		const char *add_branch;
 		const char *other_branch;
-		unsigned mode;
-		const struct object_id *oid;
 		const char *conf;
+		const struct diff_filespec *contents;
 
-		if (a_oid) {
-			add_branch = o->branch1;
-			other_branch = o->branch2;
-			mode = a_mode;
-			oid = a_oid;
+		if (a_valid) {
+			add_branch = opt->branch1;
+			other_branch = opt->branch2;
+			contents = a;
 			conf = _("file/directory");
 		} else {
-			add_branch = o->branch2;
-			other_branch = o->branch1;
-			mode = b_mode;
-			oid = b_oid;
+			add_branch = opt->branch2;
+			other_branch = opt->branch1;
+			contents = b;
 			conf = _("directory/file");
 		}
-		if (dir_in_way(o->repo->index, path,
-			       !o->call_depth && !S_ISGITLINK(a_mode),
+		if (dir_in_way(opt->repo->index, path,
+			       !opt->call_depth && !S_ISGITLINK(a->mode),
 			       0)) {
-			char *new_path = unique_path(o, path, add_branch);
+			char *new_path = unique_path(opt, path, add_branch);
 			clean_merge = 0;
-			output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
+			output(opt, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
 			       "Adding %s as %s"),
 			       conf, path, other_branch, path, new_path);
-			if (update_file(o, 0, oid, mode, new_path))
+			if (update_file(opt, 0, contents, new_path))
 				clean_merge = -1;
-			else if (o->call_depth)
-				remove_file_from_index(o->repo->index, path);
+			else if (opt->call_depth)
+				remove_file_from_index(opt->repo->index, path);
 			free(new_path);
 		} else {
-			output(o, 2, _("Adding %s"), path);
+			output(opt, 2, _("Adding %s"), path);
 			/* do not overwrite file if already present */
-			if (update_file_flags(o, oid, mode, path, 1, !a_oid))
+			if (update_file_flags(opt, contents, path, 1, !a_valid))
 				clean_merge = -1;
 		}
-	} else if (a_oid && b_oid) {
-		if (!o_oid) {
+	} else if (a_valid && b_valid) {
+		if (!o_valid) {
 			/* Case C: Added in both (check for same permissions) */
-			output(o, 1,
+			output(opt, 1,
 			       _("CONFLICT (add/add): Merge conflict in %s"),
 			       path);
-			clean_merge = handle_file_collision(o,
+			clean_merge = handle_file_collision(opt,
 							    path, NULL, NULL,
-							    o->branch1,
-							    o->branch2,
-							    a_oid, a_mode,
-							    b_oid, b_mode);
+							    opt->branch1,
+							    opt->branch2,
+							    a, b);
 		} else {
 			/* case D: Modified in both, but differently. */
+			struct merge_file_info mfi;
 			int is_dirty = 0; /* unpack_trees would have bailed if dirty */
-			clean_merge = handle_content_merge(o, path,
+			clean_merge = handle_content_merge(&mfi, opt, path,
 							   is_dirty,
-							   o_oid, o_mode,
-							   a_oid, a_mode,
-							   b_oid, b_mode,
-							   NULL);
+							   o, a, b, NULL);
 		}
-	} else if (!o_oid && !a_oid && !b_oid) {
+	} else if (!o_valid && !a_valid && !b_valid) {
 		/*
 		 * this entry was deleted altogether. a_mode == 0 means
 		 * we had that path and want to actively remove it.
 		 */
-		remove_file(o, 1, path, !a_mode);
+		remove_file(opt, 1, path, !a->mode);
 	} else
 		BUG("fatal merge failure, shouldn't happen.");
 
 	return clean_merge;
 }
 
-int merge_trees(struct merge_options *o,
+int merge_trees(struct merge_options *opt,
 		struct tree *head,
 		struct tree *merge,
 		struct tree *common,
 		struct tree **result)
 {
-	struct index_state *istate = o->repo->index;
+	struct index_state *istate = opt->repo->index;
 	int code, clean;
 	struct strbuf sb = STRBUF_INIT;
 
-	if (!o->call_depth && repo_index_has_changes(o->repo, head, &sb)) {
-		err(o, _("Your local changes to the following files would be overwritten by merge:\n  %s"),
+	if (!opt->call_depth && repo_index_has_changes(opt->repo, head, &sb)) {
+		err(opt, _("Your local changes to the following files would be overwritten by merge:\n  %s"),
 		    sb.buf);
 		return -1;
 	}
 
-	if (o->subtree_shift) {
-		merge = shift_tree_object(o->repo, head, merge, o->subtree_shift);
-		common = shift_tree_object(o->repo, head, common, o->subtree_shift);
+	if (opt->subtree_shift) {
+		merge = shift_tree_object(opt->repo, head, merge, opt->subtree_shift);
+		common = shift_tree_object(opt->repo, head, common, opt->subtree_shift);
 	}
 
 	if (oid_eq(&common->object.oid, &merge->object.oid)) {
-		output(o, 0, _("Already up to date!"));
+		output(opt, 0, _("Already up to date!"));
 		*result = head;
 		return 1;
 	}
 
-	code = unpack_trees_start(o, common, head, merge);
+	code = unpack_trees_start(opt, common, head, merge);
 
 	if (code != 0) {
-		if (show(o, 4) || o->call_depth)
-			err(o, _("merging of trees %s and %s failed"),
+		if (show(opt, 4) || opt->call_depth)
+			err(opt, _("merging of trees %s and %s failed"),
 			    oid_to_hex(&head->object.oid),
 			    oid_to_hex(&merge->object.oid));
-		unpack_trees_finish(o);
+		unpack_trees_finish(opt);
 		return -1;
 	}
 
@@ -3447,21 +3420,21 @@
 		 * opposed to decaring a local hashmap is for convenience
 		 * so that we don't have to pass it to around.
 		 */
-		hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);
-		get_files_dirs(o, head);
-		get_files_dirs(o, merge);
+		hashmap_init(&opt->current_file_dir_set, path_hashmap_cmp, NULL, 512);
+		get_files_dirs(opt, head);
+		get_files_dirs(opt, merge);
 
-		entries = get_unmerged(o->repo->index);
-		clean = detect_and_process_renames(o, common, head, merge,
+		entries = get_unmerged(opt->repo->index);
+		clean = detect_and_process_renames(opt, common, head, merge,
 						   entries, &re_info);
-		record_df_conflict_files(o, entries);
+		record_df_conflict_files(opt, entries);
 		if (clean < 0)
 			goto cleanup;
 		for (i = entries->nr-1; 0 <= i; i--) {
 			const char *path = entries->items[i].string;
 			struct stage_data *e = entries->items[i].util;
 			if (!e->processed) {
-				int ret = process_entry(o, path, e);
+				int ret = process_entry(opt, path, e);
 				if (!ret)
 					clean = 0;
 				else if (ret < 0) {
@@ -3483,19 +3456,19 @@
 		string_list_clear(entries, 1);
 		free(entries);
 
-		hashmap_free(&o->current_file_dir_set, 1);
+		hashmap_free(&opt->current_file_dir_set, 1);
 
 		if (clean < 0) {
-			unpack_trees_finish(o);
+			unpack_trees_finish(opt);
 			return clean;
 		}
 	}
 	else
 		clean = 1;
 
-	unpack_trees_finish(o);
+	unpack_trees_finish(opt);
 
-	if (o->call_depth && !(*result = write_tree_from_memory(o)))
+	if (opt->call_depth && !(*result = write_tree_from_memory(opt)))
 		return -1;
 
 	return clean;
@@ -3516,7 +3489,7 @@
  * Merge the commits h1 and h2, return the resulting virtual
  * commit object and a flag indicating the cleanness of the merge.
  */
-int merge_recursive(struct merge_options *o,
+int merge_recursive(struct merge_options *opt,
 		    struct commit *h1,
 		    struct commit *h2,
 		    struct commit_list *ca,
@@ -3527,10 +3500,10 @@
 	struct tree *mrtree;
 	int clean;
 
-	if (show(o, 4)) {
-		output(o, 4, _("Merging:"));
-		output_commit_title(o, h1);
-		output_commit_title(o, h2);
+	if (show(opt, 4)) {
+		output(opt, 4, _("Merging:"));
+		output_commit_title(opt, h1);
+		output_commit_title(opt, h2);
 	}
 
 	if (!ca) {
@@ -3538,13 +3511,13 @@
 		ca = reverse_commit_list(ca);
 	}
 
-	if (show(o, 5)) {
+	if (show(opt, 5)) {
 		unsigned cnt = commit_list_count(ca);
 
-		output(o, 5, Q_("found %u common ancestor:",
+		output(opt, 5, Q_("found %u common ancestor:",
 				"found %u common ancestors:", cnt), cnt);
 		for (iter = ca; iter; iter = iter->next)
-			output_commit_title(o, iter->item);
+			output_commit_title(opt, iter->item);
 	}
 
 	merged_common_ancestors = pop_commit(&ca);
@@ -3552,13 +3525,13 @@
 		/* if there is no common ancestor, use an empty tree */
 		struct tree *tree;
 
-		tree = lookup_tree(o->repo, o->repo->hash_algo->empty_tree);
-		merged_common_ancestors = make_virtual_commit(o->repo, tree, "ancestor");
+		tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);
+		merged_common_ancestors = make_virtual_commit(opt->repo, tree, "ancestor");
 	}
 
 	for (iter = ca; iter; iter = iter->next) {
 		const char *saved_b1, *saved_b2;
-		o->call_depth++;
+		opt->call_depth++;
 		/*
 		 * When the merge fails, the result contains files
 		 * with conflict markers. The cleanness flag is
@@ -3567,46 +3540,46 @@
 		 * overwritten it: the committed "conflicts" were
 		 * already resolved.
 		 */
-		discard_index(o->repo->index);
-		saved_b1 = o->branch1;
-		saved_b2 = o->branch2;
-		o->branch1 = "Temporary merge branch 1";
-		o->branch2 = "Temporary merge branch 2";
-		if (merge_recursive(o, merged_common_ancestors, iter->item,
+		discard_index(opt->repo->index);
+		saved_b1 = opt->branch1;
+		saved_b2 = opt->branch2;
+		opt->branch1 = "Temporary merge branch 1";
+		opt->branch2 = "Temporary merge branch 2";
+		if (merge_recursive(opt, merged_common_ancestors, iter->item,
 				    NULL, &merged_common_ancestors) < 0)
 			return -1;
-		o->branch1 = saved_b1;
-		o->branch2 = saved_b2;
-		o->call_depth--;
+		opt->branch1 = saved_b1;
+		opt->branch2 = saved_b2;
+		opt->call_depth--;
 
 		if (!merged_common_ancestors)
-			return err(o, _("merge returned no commit"));
+			return err(opt, _("merge returned no commit"));
 	}
 
-	discard_index(o->repo->index);
-	if (!o->call_depth)
-		repo_read_index(o->repo);
+	discard_index(opt->repo->index);
+	if (!opt->call_depth)
+		repo_read_index(opt->repo);
 
-	o->ancestor = "merged common ancestors";
-	clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),
+	opt->ancestor = "merged common ancestors";
+	clean = merge_trees(opt, get_commit_tree(h1), get_commit_tree(h2),
 			    get_commit_tree(merged_common_ancestors),
 			    &mrtree);
 	if (clean < 0) {
-		flush_output(o);
+		flush_output(opt);
 		return clean;
 	}
 
-	if (o->call_depth) {
-		*result = make_virtual_commit(o->repo, mrtree, "merged tree");
+	if (opt->call_depth) {
+		*result = make_virtual_commit(opt->repo, mrtree, "merged tree");
 		commit_list_insert(h1, &(*result)->parents);
 		commit_list_insert(h2, &(*result)->parents->next);
 	}
-	flush_output(o);
-	if (!o->call_depth && o->buffer_output < 2)
-		strbuf_release(&o->obuf);
-	if (show(o, 2))
+	flush_output(opt);
+	if (!opt->call_depth && opt->buffer_output < 2)
+		strbuf_release(&opt->obuf);
+	if (show(opt, 2))
 		diff_warn_rename_limit("merge.renamelimit",
-				       o->needed_rename_limit, 0);
+				       opt->needed_rename_limit, 0);
 	return clean;
 }
 
@@ -3628,7 +3601,7 @@
 	return (struct commit *)object;
 }
 
-int merge_recursive_generic(struct merge_options *o,
+int merge_recursive_generic(struct merge_options *opt,
 			    const struct object_id *head,
 			    const struct object_id *merge,
 			    int num_base_list,
@@ -3637,127 +3610,136 @@
 {
 	int clean;
 	struct lock_file lock = LOCK_INIT;
-	struct commit *head_commit = get_ref(o->repo, head, o->branch1);
-	struct commit *next_commit = get_ref(o->repo, merge, o->branch2);
+	struct commit *head_commit = get_ref(opt->repo, head, opt->branch1);
+	struct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);
 	struct commit_list *ca = NULL;
 
 	if (base_list) {
 		int i;
 		for (i = 0; i < num_base_list; ++i) {
 			struct commit *base;
-			if (!(base = get_ref(o->repo, base_list[i], oid_to_hex(base_list[i]))))
-				return err(o, _("Could not parse object '%s'"),
+			if (!(base = get_ref(opt->repo, base_list[i], oid_to_hex(base_list[i]))))
+				return err(opt, _("Could not parse object '%s'"),
 					   oid_to_hex(base_list[i]));
 			commit_list_insert(base, &ca);
 		}
 	}
 
-	repo_hold_locked_index(o->repo, &lock, LOCK_DIE_ON_ERROR);
-	clean = merge_recursive(o, head_commit, next_commit, ca,
+	repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
+	clean = merge_recursive(opt, head_commit, next_commit, ca,
 				result);
 	if (clean < 0) {
 		rollback_lock_file(&lock);
 		return clean;
 	}
 
-	if (write_locked_index(o->repo->index, &lock,
+	if (write_locked_index(opt->repo->index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
-		return err(o, _("Unable to write index."));
+		return err(opt, _("Unable to write index."));
 
 	return clean ? 0 : 1;
 }
 
-static void merge_recursive_config(struct merge_options *o)
+static void merge_recursive_config(struct merge_options *opt)
 {
 	char *value = NULL;
-	git_config_get_int("merge.verbosity", &o->verbosity);
-	git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
-	git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
+	git_config_get_int("merge.verbosity", &opt->verbosity);
+	git_config_get_int("diff.renamelimit", &opt->diff_rename_limit);
+	git_config_get_int("merge.renamelimit", &opt->merge_rename_limit);
 	if (!git_config_get_string("diff.renames", &value)) {
-		o->diff_detect_rename = git_config_rename("diff.renames", value);
+		opt->diff_detect_rename = git_config_rename("diff.renames", value);
 		free(value);
 	}
 	if (!git_config_get_string("merge.renames", &value)) {
-		o->merge_detect_rename = git_config_rename("merge.renames", value);
+		opt->merge_detect_rename = git_config_rename("merge.renames", value);
+		free(value);
+	}
+	if (!git_config_get_string("merge.directoryrenames", &value)) {
+		int boolval = git_parse_maybe_bool(value);
+		if (0 <= boolval) {
+			opt->detect_directory_renames = boolval ? 2 : 0;
+		} else if (!strcasecmp(value, "conflict")) {
+			opt->detect_directory_renames = 1;
+		} /* avoid erroring on values from future versions of git */
 		free(value);
 	}
 	git_config(git_xmerge_config, NULL);
 }
 
-void init_merge_options(struct merge_options *o,
+void init_merge_options(struct merge_options *opt,
 			struct repository *repo)
 {
 	const char *merge_verbosity;
-	memset(o, 0, sizeof(struct merge_options));
-	o->repo = repo;
-	o->verbosity = 2;
-	o->buffer_output = 1;
-	o->diff_rename_limit = -1;
-	o->merge_rename_limit = -1;
-	o->renormalize = 0;
-	o->diff_detect_rename = -1;
-	o->merge_detect_rename = -1;
-	o->detect_directory_renames = 1;
-	merge_recursive_config(o);
+	memset(opt, 0, sizeof(struct merge_options));
+	opt->repo = repo;
+	opt->verbosity = 2;
+	opt->buffer_output = 1;
+	opt->diff_rename_limit = -1;
+	opt->merge_rename_limit = -1;
+	opt->renormalize = 0;
+	opt->diff_detect_rename = -1;
+	opt->merge_detect_rename = -1;
+	opt->detect_directory_renames = 1;
+	merge_recursive_config(opt);
 	merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
 	if (merge_verbosity)
-		o->verbosity = strtol(merge_verbosity, NULL, 10);
-	if (o->verbosity >= 5)
-		o->buffer_output = 0;
-	strbuf_init(&o->obuf, 0);
-	string_list_init(&o->df_conflict_file_set, 1);
+		opt->verbosity = strtol(merge_verbosity, NULL, 10);
+	if (opt->verbosity >= 5)
+		opt->buffer_output = 0;
+	strbuf_init(&opt->obuf, 0);
+	string_list_init(&opt->df_conflict_file_set, 1);
 }
 
-int parse_merge_opt(struct merge_options *o, const char *s)
+int parse_merge_opt(struct merge_options *opt, const char *s)
 {
 	const char *arg;
 
 	if (!s || !*s)
 		return -1;
 	if (!strcmp(s, "ours"))
-		o->recursive_variant = MERGE_RECURSIVE_OURS;
+		opt->recursive_variant = MERGE_RECURSIVE_OURS;
 	else if (!strcmp(s, "theirs"))
-		o->recursive_variant = MERGE_RECURSIVE_THEIRS;
+		opt->recursive_variant = MERGE_RECURSIVE_THEIRS;
 	else if (!strcmp(s, "subtree"))
-		o->subtree_shift = "";
+		opt->subtree_shift = "";
 	else if (skip_prefix(s, "subtree=", &arg))
-		o->subtree_shift = arg;
+		opt->subtree_shift = arg;
 	else if (!strcmp(s, "patience"))
-		o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
+		opt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);
 	else if (!strcmp(s, "histogram"))
-		o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
+		opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
 	else if (skip_prefix(s, "diff-algorithm=", &arg)) {
 		long value = parse_algorithm_value(arg);
 		if (value < 0)
 			return -1;
 		/* clear out previous settings */
-		DIFF_XDL_CLR(o, NEED_MINIMAL);
-		o->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
-		o->xdl_opts |= value;
+		DIFF_XDL_CLR(opt, NEED_MINIMAL);
+		opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+		opt->xdl_opts |= value;
 	}
 	else if (!strcmp(s, "ignore-space-change"))
-		DIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE);
+		DIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);
 	else if (!strcmp(s, "ignore-all-space"))
-		DIFF_XDL_SET(o, IGNORE_WHITESPACE);
+		DIFF_XDL_SET(opt, IGNORE_WHITESPACE);
 	else if (!strcmp(s, "ignore-space-at-eol"))
-		DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);
+		DIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);
 	else if (!strcmp(s, "ignore-cr-at-eol"))
-		DIFF_XDL_SET(o, IGNORE_CR_AT_EOL);
+		DIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);
 	else if (!strcmp(s, "renormalize"))
-		o->renormalize = 1;
+		opt->renormalize = 1;
 	else if (!strcmp(s, "no-renormalize"))
-		o->renormalize = 0;
+		opt->renormalize = 0;
 	else if (!strcmp(s, "no-renames"))
-		o->merge_detect_rename = 0;
+		opt->merge_detect_rename = 0;
 	else if (!strcmp(s, "find-renames")) {
-		o->merge_detect_rename = 1;
-		o->rename_score = 0;
+		opt->merge_detect_rename = 1;
+		opt->rename_score = 0;
 	}
 	else if (skip_prefix(s, "find-renames=", &arg) ||
 		 skip_prefix(s, "rename-threshold=", &arg)) {
-		if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
+		if ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
 			return -1;
-		o->merge_detect_rename = 1;
+		opt->merge_detect_rename = 1;
 	}
 	/*
 	 * Please update $__git_merge_strategy_options in
diff --git a/midx.c b/midx.c
index f1137eb..d5d2e95 100644
--- a/midx.c
+++ b/midx.c
@@ -311,7 +311,39 @@
 	return nth_midxed_pack_entry(m, e, pos);
 }
 
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_name)
+/* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */
+static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
+				const char *idx_name)
+{
+	/* Skip past any initial matching prefix. */
+	while (*idx_name && *idx_name == *idx_or_pack_name) {
+		idx_name++;
+		idx_or_pack_name++;
+	}
+
+	/*
+	 * If we didn't match completely, we may have matched "pack-1234." and
+	 * be left with "idx" and "pack" respectively, which is also OK. We do
+	 * not have to check for "idx" and "idx", because that would have been
+	 * a complete match (and in that case these strcmps will be false, but
+	 * we'll correctly return 0 from the final strcmp() below.
+	 *
+	 * Technically this matches "fooidx" and "foopack", but we'd never have
+	 * such names in the first place.
+	 */
+	if (!strcmp(idx_name, "idx") && !strcmp(idx_or_pack_name, "pack"))
+		return 0;
+
+	/*
+	 * This not only checks for a complete match, but also orders based on
+	 * the first non-identical character, which means our ordering will
+	 * match a raw strcmp(). That makes it OK to use this to binary search
+	 * a naively-sorted list.
+	 */
+	return strcmp(idx_or_pack_name, idx_name);
+}
+
+int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
 {
 	uint32_t first = 0, last = m->num_packs;
 
@@ -321,7 +353,7 @@
 		int cmp;
 
 		current = m->pack_names[mid];
-		cmp = strcmp(idx_name, current);
+		cmp = cmp_idx_or_pack_name(idx_or_pack_name, current);
 		if (!cmp)
 			return 1;
 		if (cmp > 0) {
diff --git a/midx.h b/midx.h
index 774f652..26dd042 100644
--- a/midx.h
+++ b/midx.h
@@ -43,7 +43,7 @@
 					struct multi_pack_index *m,
 					uint32_t n);
 int fill_midx_entry(const struct object_id *oid, struct pack_entry *e, struct multi_pack_index *m);
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_name);
+int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
 
 int write_midx_file(const char *object_dir);
diff --git a/notes-merge.c b/notes-merge.c
index 280aa8e..2fe724f 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -29,14 +29,14 @@
 
 static int path_to_oid(const char *path, struct object_id *oid)
 {
-	char hex_oid[GIT_SHA1_HEXSZ];
+	char hex_oid[GIT_MAX_HEXSZ];
 	int i = 0;
-	while (*path && i < GIT_SHA1_HEXSZ) {
+	while (*path && i < the_hash_algo->hexsz) {
 		if (*path != '/')
 			hex_oid[i++] = *path;
 		path++;
 	}
-	if (*path || i != GIT_SHA1_HEXSZ)
+	if (*path || i != the_hash_algo->hexsz)
 		return -1;
 	return get_oid_hex(hex_oid, oid);
 }
diff --git a/notes.c b/notes.c
index 7f7cc4d..532ec37 100644
--- a/notes.c
+++ b/notes.c
@@ -67,8 +67,9 @@
 
 #define GET_NIBBLE(n, sha1) ((((sha1)[(n) >> 1]) >> ((~(n) & 0x01) << 2)) & 0x0f)
 
-#define KEY_INDEX (GIT_SHA1_RAWSZ - 1)
-#define FANOUT_PATH_SEPARATORS ((GIT_SHA1_HEXSZ / 2) - 1)
+#define KEY_INDEX (the_hash_algo->rawsz - 1)
+#define FANOUT_PATH_SEPARATORS (the_hash_algo->rawsz - 1)
+#define FANOUT_PATH_SEPARATORS_MAX ((GIT_MAX_HEXSZ / 2) - 1)
 #define SUBTREE_SHA1_PREFIXCMP(key_sha1, subtree_sha1) \
 	(memcmp(key_sha1, subtree_sha1, subtree_sha1[KEY_INDEX]))
 
@@ -198,7 +199,7 @@
 		struct leaf_node *entry)
 {
 	struct leaf_node *l;
-	struct int_node *parent_stack[GIT_SHA1_RAWSZ];
+	struct int_node *parent_stack[GIT_MAX_RAWSZ];
 	unsigned char i, j;
 	void **p = note_tree_search(t, &tree, &n, entry->key_oid.hash);
 
@@ -394,6 +395,7 @@
 	void *buf;
 	struct tree_desc desc;
 	struct name_entry entry;
+	const unsigned hashsz = the_hash_algo->rawsz;
 
 	buf = fill_tree_descriptor(&desc, &subtree->val_oid);
 	if (!buf)
@@ -401,7 +403,7 @@
 		     oid_to_hex(&subtree->val_oid));
 
 	prefix_len = subtree->key_oid.hash[KEY_INDEX];
-	if (prefix_len >= GIT_SHA1_RAWSZ)
+	if (prefix_len >= hashsz)
 		BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len);
 	if (prefix_len * 2 < n)
 		BUG("prefix_len (%"PRIuMAX") is too small", (uintmax_t)prefix_len);
@@ -411,7 +413,7 @@
 		struct leaf_node *l;
 		size_t path_len = strlen(entry.path);
 
-		if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) {
+		if (path_len == 2 * (hashsz - prefix_len)) {
 			/* This is potentially the remainder of the SHA-1 */
 
 			if (!S_ISREG(entry.mode))
@@ -419,7 +421,7 @@
 				goto handle_non_note;
 
 			if (hex_to_bytes(object_oid.hash + prefix_len, entry.path,
-					 GIT_SHA1_RAWSZ - prefix_len))
+					 hashsz - prefix_len))
 				goto handle_non_note; /* entry.path is not a SHA1 */
 
 			type = PTR_TYPE_NOTE;
@@ -439,7 +441,7 @@
 			 * except for the last byte, where we write
 			 * the length:
 			 */
-			memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1);
+			memset(object_oid.hash + len, 0, hashsz - len - 1);
 			object_oid.hash[KEY_INDEX] = (unsigned char)len;
 
 			type = PTR_TYPE_SUBTREE;
@@ -527,22 +529,22 @@
 	return fanout + 1;
 }
 
-/* hex SHA1 + 19 * '/' + NUL */
-#define FANOUT_PATH_MAX GIT_SHA1_HEXSZ + FANOUT_PATH_SEPARATORS + 1
+/* hex oid + '/' between each pair of hex digits + NUL */
+#define FANOUT_PATH_MAX GIT_MAX_HEXSZ + FANOUT_PATH_SEPARATORS_MAX + 1
 
-static void construct_path_with_fanout(const unsigned char *sha1,
+static void construct_path_with_fanout(const unsigned char *hash,
 		unsigned char fanout, char *path)
 {
 	unsigned int i = 0, j = 0;
-	const char *hex_sha1 = sha1_to_hex(sha1);
-	assert(fanout < GIT_SHA1_RAWSZ);
+	const char *hex_hash = hash_to_hex(hash);
+	assert(fanout < the_hash_algo->rawsz);
 	while (fanout) {
-		path[i++] = hex_sha1[j++];
-		path[i++] = hex_sha1[j++];
+		path[i++] = hex_hash[j++];
+		path[i++] = hex_hash[j++];
 		path[i++] = '/';
 		fanout--;
 	}
-	xsnprintf(path + i, FANOUT_PATH_MAX - i, "%s", hex_sha1 + j);
+	xsnprintf(path + i, FANOUT_PATH_MAX - i, "%s", hex_hash + j);
 }
 
 static int for_each_note_helper(struct notes_tree *t, struct int_node *tree,
@@ -637,10 +639,10 @@
 
 static void write_tree_entry(struct strbuf *buf, unsigned int mode,
 		const char *path, unsigned int path_len, const
-		unsigned char *sha1)
+		unsigned char *hash)
 {
 	strbuf_addf(buf, "%o %.*s%c", mode, path_len, path, '\0');
-	strbuf_add(buf, sha1, GIT_SHA1_RAWSZ);
+	strbuf_add(buf, hash, the_hash_algo->rawsz);
 }
 
 static void tree_write_stack_init_subtree(struct tree_write_stack *tws,
@@ -652,7 +654,7 @@
 	n = (struct tree_write_stack *)
 		xmalloc(sizeof(struct tree_write_stack));
 	n->next = NULL;
-	strbuf_init(&n->buf, 256 * (32 + GIT_SHA1_HEXSZ)); /* assume 256 entries per tree */
+	strbuf_init(&n->buf, 256 * (32 + the_hash_algo->hexsz)); /* assume 256 entries per tree */
 	n->path[0] = n->path[1] = '\0';
 	tws->next = n;
 	tws->path[0] = path[0];
@@ -757,7 +759,7 @@
 		note_path[note_path_len] = '\0';
 		mode = 040000;
 	}
-	assert(note_path_len <= GIT_SHA1_HEXSZ + FANOUT_PATH_SEPARATORS);
+	assert(note_path_len <= GIT_MAX_HEXSZ + FANOUT_PATH_SEPARATORS);
 
 	/* Weave non-note entries into note entries */
 	return  write_each_non_note_until(note_path, d) ||
@@ -986,7 +988,7 @@
 		combine_notes_fn combine_notes, int flags)
 {
 	struct object_id oid, object_oid;
-	unsigned mode;
+	unsigned short mode;
 	struct leaf_node root_tree;
 
 	if (!t)
@@ -1137,7 +1139,7 @@
 
 	/* Prepare for traversal of current notes tree */
 	root.next = NULL; /* last forward entry in list is grounded */
-	strbuf_init(&root.buf, 256 * (32 + GIT_SHA1_HEXSZ)); /* assume 256 entries */
+	strbuf_init(&root.buf, 256 * (32 + the_hash_algo->hexsz)); /* assume 256 entries */
 	root.path[0] = root.path[1] = '\0';
 	cb_data.root = &root;
 	cb_data.next_non_note = t->first_non_note;
@@ -1165,7 +1167,7 @@
 
 	while (l) {
 		if (flags & NOTES_PRUNE_VERBOSE)
-			printf("%s\n", sha1_to_hex(l->sha1));
+			printf("%s\n", hash_to_hex(l->sha1));
 		if (!(flags & NOTES_PRUNE_DRYRUN))
 			remove_note(t, l->sha1);
 		l = l->next;
diff --git a/object-store.h b/object-store.h
index 14fc935..46a1da7 100644
--- a/object-store.h
+++ b/object-store.h
@@ -77,7 +77,7 @@
 		 freshened:1,
 		 do_not_close:1,
 		 pack_promisor:1;
-	unsigned char sha1[20];
+	unsigned char hash[GIT_MAX_RAWSZ];
 	struct revindex_entry *revindex;
 	/* something like ".git/objects/pack/xxxxx.pack" */
 	char pack_name[FLEX_ARRAY]; /* more */
@@ -162,10 +162,10 @@
 void *map_loose_object(struct repository *r, const struct object_id *oid,
 		       unsigned long *size);
 
-extern void *read_object_file_extended(struct repository *r,
-				       const struct object_id *oid,
-				       enum object_type *type,
-				       unsigned long *size, int lookup_replace);
+void *read_object_file_extended(struct repository *r,
+				const struct object_id *oid,
+				enum object_type *type,
+				unsigned long *size, int lookup_replace);
 static inline void *repo_read_object_file(struct repository *r,
 					  const struct object_id *oid,
 					  enum object_type *type,
@@ -180,20 +180,20 @@
 /* Read and unpack an object file into memory, write memory to an object file */
 int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
 
-extern int hash_object_file(const void *buf, unsigned long len,
-			    const char *type, struct object_id *oid);
+int hash_object_file(const void *buf, unsigned long len,
+		     const char *type, struct object_id *oid);
 
-extern int write_object_file(const void *buf, unsigned long len,
-			     const char *type, struct object_id *oid);
+int write_object_file(const void *buf, unsigned long len,
+		      const char *type, struct object_id *oid);
 
-extern int hash_object_file_literally(const void *buf, unsigned long len,
-				      const char *type, struct object_id *oid,
-				      unsigned flags);
+int hash_object_file_literally(const void *buf, unsigned long len,
+			       const char *type, struct object_id *oid,
+			       unsigned flags);
 
-extern int pretend_object_file(void *, unsigned long, enum object_type,
-			       struct object_id *oid);
+int pretend_object_file(void *, unsigned long, enum object_type,
+			struct object_id *oid);
 
-extern int force_object_loose(const struct object_id *oid, time_t mtime);
+int force_object_loose(const struct object_id *oid, time_t mtime);
 
 /*
  * Open the loose object at path, check its hash, and return the contents,
@@ -227,9 +227,9 @@
  * with the specified name.  This function does not respect replace
  * references.
  */
-extern int has_loose_object_nonlocal(const struct object_id *);
+int has_loose_object_nonlocal(const struct object_id *);
 
-extern void assert_oid_type(const struct object_id *oid, enum object_type expect);
+void assert_oid_type(const struct object_id *oid, enum object_type expect);
 
 struct object_info {
 	/* Request */
@@ -280,6 +280,12 @@
 #define OBJECT_INFO_QUICK 8
 /* Do not check loose object */
 #define OBJECT_INFO_IGNORE_LOOSE 16
+/*
+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is
+ * nonzero). This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_QUICK.
+ */
+#define OBJECT_INFO_FOR_PREFETCH (32 + OBJECT_INFO_QUICK)
 
 int oid_object_info_extended(struct repository *r,
 			     const struct object_id *,
diff --git a/object.h b/object.h
index 796792c..4526979 100644
--- a/object.h
+++ b/object.h
@@ -90,19 +90,19 @@
 	struct object_id oid;
 };
 
-extern const char *type_name(unsigned int type);
-extern int type_from_string_gently(const char *str, ssize_t, int gentle);
+const char *type_name(unsigned int type);
+int type_from_string_gently(const char *str, ssize_t, int gentle);
 #define type_from_string(str) type_from_string_gently(str, -1, 0)
 
 /*
  * Return the current number of buckets in the object hashmap.
  */
-extern unsigned int get_max_object_index(void);
+unsigned int get_max_object_index(void);
 
 /*
  * Return the object from the specified bucket in the object hashmap.
  */
-extern struct object *get_indexed_object(unsigned int);
+struct object *get_indexed_object(unsigned int);
 
 /*
  * This can be used to see if we have heard of the object before, but
@@ -118,7 +118,7 @@
  */
 struct object *lookup_object(struct repository *r, const unsigned char *sha1);
 
-extern void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
+void *create_object(struct repository *r, const unsigned char *sha1, void *obj);
 
 void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet);
 
@@ -189,6 +189,6 @@
 /*
  * Clear the specified object flags from all in-core commit objects.
  */
-extern void clear_commit_marks_all(unsigned int flags);
+void clear_commit_marks_all(unsigned int flags);
 
 #endif /* OBJECT_H */
diff --git a/oidmap.h b/oidmap.h
index 72430b6..7a93946 100644
--- a/oidmap.h
+++ b/oidmap.h
@@ -33,7 +33,7 @@
  * parameter may be used to preallocate a sufficiently large table and thus
  * prevent expensive resizing. If 0, the table is dynamically resized.
  */
-extern void oidmap_init(struct oidmap *map, size_t initial_size);
+void oidmap_init(struct oidmap *map, size_t initial_size);
 
 /*
  * Frees an oidmap structure and allocated memory.
@@ -41,13 +41,13 @@
  * If `free_entries` is true, each oidmap_entry in the map is freed as well
  * using stdlibs free().
  */
-extern void oidmap_free(struct oidmap *map, int free_entries);
+void oidmap_free(struct oidmap *map, int free_entries);
 
 /*
  * Returns the oidmap entry for the specified oid, or NULL if not found.
  */
-extern void *oidmap_get(const struct oidmap *map,
-			const struct object_id *key);
+void *oidmap_get(const struct oidmap *map,
+		 const struct object_id *key);
 
 /*
  * Adds or replaces an oidmap entry.
@@ -57,14 +57,14 @@
  *
  * Returns the replaced entry, or NULL if not found (i.e. the entry was added).
  */
-extern void *oidmap_put(struct oidmap *map, void *entry);
+void *oidmap_put(struct oidmap *map, void *entry);
 
 /*
  * Removes an oidmap entry matching the specified oid.
  *
  * Returns the removed entry, or NULL if not found.
  */
-extern void *oidmap_remove(struct oidmap *map, const struct object_id *key);
+void *oidmap_remove(struct oidmap *map, const struct object_id *key);
 
 
 struct oidmap_iter {
diff --git a/oidset.h b/oidset.h
index c9d0f6d..14f18f7 100644
--- a/oidset.h
+++ b/oidset.h
@@ -16,18 +16,6 @@
  *      table overhead.
  */
 
-static inline unsigned int oid_hash(struct object_id oid)
-{
-	return sha1hash(oid.hash);
-}
-
-static inline int oid_equal(struct object_id a, struct object_id b)
-{
-	return oideq(&a, &b);
-}
-
-KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
-
 /**
  * A single oidset; should be zero-initialized (or use OIDSET_INIT).
  */
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 5566e94..802ed62 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -142,13 +142,13 @@
 	seen_objects_nr = 0;
 }
 
-static uint32_t find_object_pos(const unsigned char *sha1)
+static uint32_t find_object_pos(const unsigned char *hash)
 {
-	struct object_entry *entry = packlist_find(writer.to_pack, sha1, NULL);
+	struct object_entry *entry = packlist_find(writer.to_pack, hash, NULL);
 
 	if (!entry) {
 		die("Failed to write bitmap index. Packfile doesn't have full closure "
-			"(object %s is missing)", sha1_to_hex(sha1));
+			"(object %s is missing)", hash_to_hex(hash));
 	}
 
 	return oe_in_pack_pos(writer.to_pack, entry);
@@ -535,7 +535,7 @@
 	header.entry_count = htonl(writer.selected_nr);
 	hashcpy(header.checksum, writer.pack_checksum);
 
-	hashwrite(f, &header, sizeof(header));
+	hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);
 	dump_bitmap(f, writer.commits);
 	dump_bitmap(f, writer.trees);
 	dump_bitmap(f, writer.blobs);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 4695aaf..6069b2f 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -18,7 +18,7 @@
  * commit.
  */
 struct stored_bitmap {
-	unsigned char sha1[20];
+	struct object_id oid;
 	struct ewah_bitmap *root;
 	struct stored_bitmap *xor;
 	int flags;
@@ -60,8 +60,8 @@
 	struct ewah_bitmap *blobs;
 	struct ewah_bitmap *tags;
 
-	/* Map from SHA1 -> `stored_bitmap` for all the bitmapped commits */
-	khash_sha1 *bitmaps;
+	/* Map from object ID -> `stored_bitmap` for all the bitmapped commits */
+	kh_oid_map_t *bitmaps;
 
 	/* Number of bitmapped commits */
 	uint32_t entry_count;
@@ -80,7 +80,7 @@
 		struct object **objects;
 		uint32_t *hashes;
 		uint32_t count, alloc;
-		khash_sha1_pos *positions;
+		kh_oid_pos_t *positions;
 	} ext_index;
 
 	/* Bitmap result of the last performed walk */
@@ -138,7 +138,7 @@
 {
 	struct bitmap_disk_header *header = (void *)index->map;
 
-	if (index->map_size < sizeof(*header) + 20)
+	if (index->map_size < sizeof(*header) + the_hash_algo->rawsz)
 		return error("Corrupted bitmap index (missing header data)");
 
 	if (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)
@@ -157,19 +157,19 @@
 				"(Git requires BITMAP_OPT_FULL_DAG)");
 
 		if (flags & BITMAP_OPT_HASH_CACHE) {
-			unsigned char *end = index->map + index->map_size - 20;
+			unsigned char *end = index->map + index->map_size - the_hash_algo->rawsz;
 			index->hashes = ((uint32_t *)end) - index->pack->num_objects;
 		}
 	}
 
 	index->entry_count = ntohl(header->entry_count);
-	index->map_pos += sizeof(*header);
+	index->map_pos += sizeof(*header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz;
 	return 0;
 }
 
 static struct stored_bitmap *store_bitmap(struct bitmap_index *index,
 					  struct ewah_bitmap *root,
-					  const unsigned char *sha1,
+					  const unsigned char *hash,
 					  struct stored_bitmap *xor_with,
 					  int flags)
 {
@@ -181,15 +181,15 @@
 	stored->root = root;
 	stored->xor = xor_with;
 	stored->flags = flags;
-	hashcpy(stored->sha1, sha1);
+	oidread(&stored->oid, hash);
 
-	hash_pos = kh_put_sha1(index->bitmaps, stored->sha1, &ret);
+	hash_pos = kh_put_oid_map(index->bitmaps, stored->oid, &ret);
 
 	/* a 0 return code means the insertion succeeded with no changes,
 	 * because the SHA1 already existed on the map. this is bad, there
 	 * shouldn't be duplicated commits in the index */
 	if (ret == 0) {
-		error("Duplicate entry in bitmap index: %s", sha1_to_hex(sha1));
+		error("Duplicate entry in bitmap index: %s", hash_to_hex(hash));
 		return NULL;
 	}
 
@@ -306,9 +306,10 @@
 {
 	assert(bitmap_git->map);
 
-	bitmap_git->bitmaps = kh_init_sha1();
-	bitmap_git->ext_index.positions = kh_init_sha1_pos();
-	load_pack_revindex(bitmap_git->pack);
+	bitmap_git->bitmaps = kh_init_oid_map();
+	bitmap_git->ext_index.positions = kh_init_oid_pos();
+	if (load_pack_revindex(bitmap_git->pack))
+		goto failed;
 
 	if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||
 		!(bitmap_git->trees = read_bitmap_1(bitmap_git)) ||
@@ -362,10 +363,10 @@
 };
 
 static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
-					   const unsigned char *sha1)
+					   const struct object_id *oid)
 {
-	khash_sha1_pos *positions = bitmap_git->ext_index.positions;
-	khiter_t pos = kh_get_sha1_pos(positions, sha1);
+	khash_oid_pos *positions = bitmap_git->ext_index.positions;
+	khiter_t pos = kh_get_oid_pos(positions, *oid);
 
 	if (pos < kh_end(positions)) {
 		int bitmap_pos = kh_value(positions, pos);
@@ -376,9 +377,9 @@
 }
 
 static inline int bitmap_position_packfile(struct bitmap_index *bitmap_git,
-					   const unsigned char *sha1)
+					   const struct object_id *oid)
 {
-	off_t offset = find_pack_entry_one(sha1, bitmap_git->pack);
+	off_t offset = find_pack_entry_one(oid->hash, bitmap_git->pack);
 	if (!offset)
 		return -1;
 
@@ -386,10 +387,10 @@
 }
 
 static int bitmap_position(struct bitmap_index *bitmap_git,
-			   const unsigned char *sha1)
+			   const struct object_id *oid)
 {
-	int pos = bitmap_position_packfile(bitmap_git, sha1);
-	return (pos >= 0) ? pos : bitmap_position_extended(bitmap_git, sha1);
+	int pos = bitmap_position_packfile(bitmap_git, oid);
+	return (pos >= 0) ? pos : bitmap_position_extended(bitmap_git, oid);
 }
 
 static int ext_index_add_object(struct bitmap_index *bitmap_git,
@@ -401,7 +402,7 @@
 	int hash_ret;
 	int bitmap_pos;
 
-	hash_pos = kh_put_sha1_pos(eindex->positions, object->oid.hash, &hash_ret);
+	hash_pos = kh_put_oid_pos(eindex->positions, object->oid, &hash_ret);
 	if (hash_ret > 0) {
 		if (eindex->count >= eindex->alloc) {
 			eindex->alloc = (eindex->alloc + 16) * 3 / 2;
@@ -431,7 +432,7 @@
 	struct bitmap_show_data *data = data_;
 	int bitmap_pos;
 
-	bitmap_pos = bitmap_position(data->bitmap_git, object->oid.hash);
+	bitmap_pos = bitmap_position(data->bitmap_git, &object->oid);
 
 	if (bitmap_pos < 0)
 		bitmap_pos = ext_index_add_object(data->bitmap_git, object,
@@ -446,7 +447,7 @@
 
 static int add_to_include_set(struct bitmap_index *bitmap_git,
 			      struct include_data *data,
-			      const unsigned char *sha1,
+			      const struct object_id *oid,
 			      int bitmap_pos)
 {
 	khiter_t hash_pos;
@@ -457,7 +458,7 @@
 	if (bitmap_get(data->base, bitmap_pos))
 		return 0;
 
-	hash_pos = kh_get_sha1(bitmap_git->bitmaps, sha1);
+	hash_pos = kh_get_oid_map(bitmap_git->bitmaps, *oid);
 	if (hash_pos < kh_end(bitmap_git->bitmaps)) {
 		struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, hash_pos);
 		bitmap_or_ewah(data->base, lookup_stored_bitmap(st));
@@ -473,13 +474,13 @@
 	struct include_data *data = _data;
 	int bitmap_pos;
 
-	bitmap_pos = bitmap_position(data->bitmap_git, commit->object.oid.hash);
+	bitmap_pos = bitmap_position(data->bitmap_git, &commit->object.oid);
 	if (bitmap_pos < 0)
 		bitmap_pos = ext_index_add_object(data->bitmap_git,
 						  (struct object *)commit,
 						  NULL);
 
-	if (!add_to_include_set(data->bitmap_git, data, commit->object.oid.hash,
+	if (!add_to_include_set(data->bitmap_git, data, &commit->object.oid,
 				bitmap_pos)) {
 		struct commit_list *parent = commit->parents;
 
@@ -517,7 +518,7 @@
 		roots = roots->next;
 
 		if (object->type == OBJ_COMMIT) {
-			khiter_t pos = kh_get_sha1(bitmap_git->bitmaps, object->oid.hash);
+			khiter_t pos = kh_get_oid_map(bitmap_git->bitmaps, object->oid);
 
 			if (pos < kh_end(bitmap_git->bitmaps)) {
 				struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
@@ -559,7 +560,7 @@
 		int pos;
 
 		roots = roots->next;
-		pos = bitmap_position(bitmap_git, object->oid.hash);
+		pos = bitmap_position(bitmap_git, &object->oid);
 
 		if (pos < 0 || base == NULL || !bitmap_get(base, pos)) {
 			object->flags &= ~UNINTERESTING;
@@ -805,7 +806,7 @@
 
 		fprintf(stderr, "Failed to reuse at %d (%016llx)\n",
 			reuse_objects, result->words[i]);
-		fprintf(stderr, " %s\n", sha1_to_hex(sha1));
+		fprintf(stderr, " %s\n", hash_to_hex(sha1));
 	}
 #endif
 
@@ -925,7 +926,7 @@
 	struct bitmap_test_data *tdata = data;
 	int bitmap_pos;
 
-	bitmap_pos = bitmap_position(tdata->bitmap_git, object->oid.hash);
+	bitmap_pos = bitmap_position(tdata->bitmap_git, &object->oid);
 	if (bitmap_pos < 0)
 		die("Object not in bitmap: %s\n", oid_to_hex(&object->oid));
 
@@ -939,7 +940,7 @@
 	int bitmap_pos;
 
 	bitmap_pos = bitmap_position(tdata->bitmap_git,
-				     commit->object.oid.hash);
+				     &commit->object.oid);
 	if (bitmap_pos < 0)
 		die("Object not in bitmap: %s\n", oid_to_hex(&commit->object.oid));
 
@@ -966,7 +967,7 @@
 		bitmap_git->version, bitmap_git->entry_count);
 
 	root = revs->pending.objects[0].item;
-	pos = kh_get_sha1(bitmap_git->bitmaps, root->oid.hash);
+	pos = kh_get_oid_map(bitmap_git->bitmaps, root->oid);
 
 	if (pos < kh_end(bitmap_git->bitmaps)) {
 		struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
@@ -1080,7 +1081,7 @@
 					    lookup_stored_bitmap(stored),
 					    rebuild)) {
 				hash_pos = kh_put_sha1(reused_bitmaps,
-						       stored->sha1,
+						       stored->oid.hash,
 						       &hash_ret);
 				kh_value(reused_bitmaps, hash_pos) =
 					bitmap_to_ewah(rebuild);
@@ -1108,7 +1109,7 @@
 	ewah_pool_free(b->trees);
 	ewah_pool_free(b->blobs);
 	ewah_pool_free(b->tags);
-	kh_destroy_sha1(b->bitmaps);
+	kh_destroy_oid_map(b->bitmaps);
 	free(b->ext_index.objects);
 	free(b->ext_index.hashes);
 	bitmap_free(b->result);
@@ -1116,8 +1117,8 @@
 	free(b);
 }
 
-int bitmap_has_sha1_in_uninteresting(struct bitmap_index *bitmap_git,
-				     const unsigned char *sha1)
+int bitmap_has_oid_in_uninteresting(struct bitmap_index *bitmap_git,
+				    const struct object_id *oid)
 {
 	int pos;
 
@@ -1126,7 +1127,7 @@
 	if (!bitmap_git->haves)
 		return 0; /* walk had no "haves" */
 
-	pos = bitmap_position_packfile(bitmap_git, sha1);
+	pos = bitmap_position_packfile(bitmap_git, oid);
 	if (pos < 0)
 		return 0;
 
diff --git a/pack-bitmap.h b/pack-bitmap.h
index 8418ba8..ee97922 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -14,7 +14,7 @@
 	uint16_t version;
 	uint16_t options;
 	uint32_t entry_count;
-	unsigned char checksum[20];
+	unsigned char checksum[GIT_MAX_RAWSZ];
 };
 
 static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};
@@ -59,7 +59,7 @@
  * queried to see if a particular object was reachable from any of the
  * objects flagged as UNINTERESTING.
  */
-int bitmap_has_sha1_in_uninteresting(struct bitmap_index *, const unsigned char *sha1);
+int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_id *oid);
 
 void bitmap_writer_show_progress(int show);
 void bitmap_writer_set_checksum(unsigned char *sha1);
diff --git a/pack-revindex.c b/pack-revindex.c
index 50891f7..d28a7e4 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "pack-revindex.h"
 #include "object-store.h"
+#include "packfile.h"
 
 /*
  * Pack index for existing packs give us easy access to the offsets into
@@ -158,10 +159,14 @@
 	sort_revindex(p->revindex, num_ent, p->pack_size);
 }
 
-void load_pack_revindex(struct packed_git *p)
+int load_pack_revindex(struct packed_git *p)
 {
-	if (!p->revindex)
+	if (!p->revindex) {
+		if (open_pack_index(p))
+			return -1;
 		create_pack_revindex(p);
+	}
+	return 0;
 }
 
 int find_revindex_position(struct packed_git *p, off_t ofs)
@@ -188,7 +193,9 @@
 {
 	int pos;
 
-	load_pack_revindex(p);
+	if (load_pack_revindex(p))
+		return NULL;
+
 	pos = find_revindex_position(p, ofs);
 
 	if (pos < 0)
diff --git a/pack-revindex.h b/pack-revindex.h
index e262f3e..848331d 100644
--- a/pack-revindex.h
+++ b/pack-revindex.h
@@ -8,7 +8,7 @@
 	unsigned int nr;
 };
 
-void load_pack_revindex(struct packed_git *p);
+int load_pack_revindex(struct packed_git *p);
 int find_revindex_position(struct packed_git *p, off_t ofs);
 
 struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
diff --git a/pack.h b/pack.h
index da99fdd..9fc0945 100644
--- a/pack.h
+++ b/pack.h
@@ -55,7 +55,7 @@
 	uint32_t *anomaly;
 };
 
-extern void reset_pack_idx_option(struct pack_idx_option *);
+void reset_pack_idx_option(struct pack_idx_option *);
 
 /*
  * Packed object index header
@@ -79,28 +79,28 @@
 /* Note, the data argument could be NULL if object type is blob */
 typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
 
-extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
-extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
-extern int verify_pack_index(struct packed_git *);
-extern int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
-extern off_t write_pack_header(struct hashfile *f, uint32_t);
-extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
-extern char *index_pack_lockfile(int fd);
+const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
+int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
+int verify_pack_index(struct packed_git *);
+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+off_t write_pack_header(struct hashfile *f, uint32_t);
+void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
+char *index_pack_lockfile(int fd);
 
 /*
  * The "hdr" output buffer should be at least this big, which will handle sizes
  * up to 2^67.
  */
 #define MAX_PACK_OBJECT_HEADER 10
-extern int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
-					enum object_type, uintmax_t);
+int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
+				 enum object_type, uintmax_t);
 
 #define PH_ERROR_EOF		(-1)
 #define PH_ERROR_PACK_SIGNATURE	(-2)
 #define PH_ERROR_PROTOCOL	(-3)
-extern int read_pack_header(int fd, struct pack_header *);
+int read_pack_header(int fd, struct pack_header *);
 
-extern struct hashfile *create_tmp_packfile(char **pack_tmp_name);
-extern void finish_tmp_packfile(struct strbuf *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
+struct hashfile *create_tmp_packfile(char **pack_tmp_name);
+void finish_tmp_packfile(struct strbuf *name_buffer, const char *pack_tmp_name, struct pack_idx_entry **written_list, uint32_t nr_written, struct pack_idx_option *pack_idx_opts, unsigned char sha1[]);
 
 #endif
diff --git a/packfile.c b/packfile.c
index d2bcb2f..9f52af9 100644
--- a/packfile.c
+++ b/packfile.c
@@ -235,7 +235,7 @@
 	struct packed_git *p = alloc_packed_git(alloc);
 
 	memcpy(p->pack_name, path, alloc); /* includes NUL */
-	hashcpy(p->sha1, sha1);
+	hashcpy(p->hash, sha1);
 	if (check_packed_git_idx(idx_path, p)) {
 		free(p);
 		return NULL;
@@ -466,6 +466,16 @@
 #endif
 }
 
+const char *pack_basename(struct packed_git *p)
+{
+	const char *ret = strrchr(p->pack_name, '/');
+	if (ret)
+		ret = ret + 1; /* skip past slash */
+	else
+		ret = p->pack_name; /* we only have a base */
+	return ret;
+}
+
 /*
  * Do not call this directly as this leaks p->pack_fd on error return;
  * call open_packed_git() instead.
@@ -482,7 +492,7 @@
 
 	if (!p->index_data) {
 		struct multi_pack_index *m;
-		const char *pack_name = strrchr(p->pack_name, '/');
+		const char *pack_name = pack_basename(p);
 
 		for (m = the_repository->objects->multi_pack_index;
 		     m; m = m->next) {
@@ -722,8 +732,8 @@
 	p->pack_local = local;
 	p->mtime = st.st_mtime;
 	if (path_len < the_hash_algo->hexsz ||
-	    get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->sha1))
-		hashclr(p->sha1);
+	    get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))
+		hashclr(p->hash);
 	return p;
 }
 
@@ -893,25 +903,25 @@
  * all unreachable objects about to be pruned, in which case they're not really
  * interesting as a measure of repo size in the first place.
  */
-unsigned long approximate_object_count(void)
+unsigned long repo_approximate_object_count(struct repository *r)
 {
-	if (!the_repository->objects->approximate_object_count_valid) {
+	if (!r->objects->approximate_object_count_valid) {
 		unsigned long count;
 		struct multi_pack_index *m;
 		struct packed_git *p;
 
-		prepare_packed_git(the_repository);
+		prepare_packed_git(r);
 		count = 0;
-		for (m = get_multi_pack_index(the_repository); m; m = m->next)
+		for (m = get_multi_pack_index(r); m; m = m->next)
 			count += m->num_objects;
-		for (p = the_repository->objects->packed_git; p; p = p->next) {
+		for (p = r->objects->packed_git; p; p = p->next) {
 			if (open_pack_index(p))
 				continue;
 			count += p->num_objects;
 		}
-		the_repository->objects->approximate_object_count = count;
+		r->objects->approximate_object_count = count;
 	}
-	return the_repository->objects->approximate_object_count;
+	return r->objects->approximate_object_count;
 }
 
 static void *get_next_packed_git(const void *p)
@@ -2023,8 +2033,10 @@
 	uint32_t i;
 	int r = 0;
 
-	if (flags & FOR_EACH_OBJECT_PACK_ORDER)
-		load_pack_revindex(p);
+	if (flags & FOR_EACH_OBJECT_PACK_ORDER) {
+		if (load_pack_revindex(p))
+			return -1;
+	}
 
 	for (i = 0; i < p->num_objects; i++) {
 		uint32_t pos;
diff --git a/packfile.h b/packfile.h
index b1c1850..b678d35 100644
--- a/packfile.h
+++ b/packfile.h
@@ -15,23 +15,29 @@
  *
  * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
  */
-extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 
 /*
  * Return the name of the (local) packfile with the specified sha1 in
  * its name.  The return value is a pointer to memory that is
  * overwritten each time this function is called.
  */
-extern char *sha1_pack_name(const unsigned char *sha1);
+char *sha1_pack_name(const unsigned char *sha1);
 
 /*
  * Return the name of the (local) pack index file with the specified
  * sha1 in its name.  The return value is a pointer to memory that is
  * overwritten each time this function is called.
  */
-extern char *sha1_pack_index_name(const unsigned char *sha1);
+char *sha1_pack_index_name(const unsigned char *sha1);
 
-extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
+/*
+ * Return the basename of the packfile, omitting any containing directory
+ * (e.g., "pack-1234abcd[...].pack").
+ */
+const char *pack_basename(struct packed_git *p);
+
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len,
 				      const char *file_pach, void *data);
@@ -45,8 +51,8 @@
 #define PACKDIR_FILE_GARBAGE 4
 extern void (*report_garbage)(unsigned seen_bits, const char *path);
 
-extern void reprepare_packed_git(struct repository *r);
-extern void install_packed_git(struct repository *r, struct packed_git *pack);
+void reprepare_packed_git(struct repository *r);
+void install_packed_git(struct repository *r, struct packed_git *pack);
 
 struct packed_git *get_packed_git(struct repository *r);
 struct list_head *get_packed_git_mru(struct repository *r);
@@ -57,36 +63,37 @@
  * Give a rough count of objects in the repository. This sacrifices accuracy
  * for speed.
  */
-unsigned long approximate_object_count(void);
+unsigned long repo_approximate_object_count(struct repository *r);
+#define approximate_object_count() repo_approximate_object_count(the_repository)
 
-extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
-					 struct packed_git *packs);
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
+				  struct packed_git *packs);
 
-extern void pack_report(void);
+void pack_report(void);
 
 /*
  * mmap the index file for the specified packfile (if it is not
  * already mmapped).  Return 0 on success.
  */
-extern int open_pack_index(struct packed_git *);
+int open_pack_index(struct packed_git *);
 
 /*
  * munmap the index file for the specified packfile (if it is
  * currently mmapped).
  */
-extern void close_pack_index(struct packed_git *);
+void close_pack_index(struct packed_git *);
 
 int close_pack_fd(struct packed_git *p);
 
-extern uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
+uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 
-extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
-extern void close_pack_windows(struct packed_git *);
-extern void close_pack(struct packed_git *);
-extern void close_all_packs(struct raw_object_store *o);
-extern void unuse_pack(struct pack_window **);
-extern void clear_delta_base_cache(void);
-extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
+unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
+void close_pack_windows(struct packed_git *);
+void close_pack(struct packed_git *);
+void close_all_packs(struct raw_object_store *o);
+void unuse_pack(struct pack_window **);
+void clear_delta_base_cache(void);
+struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 
 /*
  * Make sure that a pointer access into an mmap'd index file is within bounds,
@@ -96,7 +103,7 @@
  * (like the 64-bit extended offset table), as we compare the size to the
  * fixed-length parts when we open the file.
  */
-extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
+void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
 
 /*
  * Perform binary search on a pack-index for a given oid. Packfile is expected to
@@ -112,59 +119,59 @@
  * at the SHA-1 within the mmapped index.  Return NULL if there is an
  * error.
  */
-extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
+const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 /*
  * Like nth_packed_object_sha1, but write the data into the object specified by
  * the the first argument.  Returns the first argument on success, and NULL on
  * error.
  */
-extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
+const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 
 /*
  * Return the offset of the nth object within the specified packfile.
  * The index must already be opened.
  */
-extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
+off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 
 /*
  * If the object named sha1 is present in the specified packfile,
  * return its offset within the packfile; otherwise, return 0.
  */
-extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
+off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 
-extern int is_pack_valid(struct packed_git *);
-extern void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *);
-extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
-extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
-extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
+int is_pack_valid(struct packed_git *);
+void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *);
+unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
+int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 
-extern void release_pack_memory(size_t);
+void release_pack_memory(size_t);
 
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
-extern int packed_object_info(struct repository *r,
-			      struct packed_git *pack,
-			      off_t offset, struct object_info *);
+int packed_object_info(struct repository *r,
+		       struct packed_git *pack,
+		       off_t offset, struct object_info *);
 
-extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
-extern const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
+void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1);
+const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1);
 
 /*
  * Iff a pack file in the given repository contains the object named by sha1,
  * return true and store its location to e.
  */
-extern int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e);
+int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e);
 
-extern int has_object_pack(const struct object_id *oid);
+int has_object_pack(const struct object_id *oid);
 
-extern int has_pack_index(const unsigned char *sha1);
+int has_pack_index(const unsigned char *sha1);
 
 /*
  * Return 1 if an object in a promisor packfile is or refers to the given
  * object, 0 otherwise.
  */
-extern int is_promisor_object(const struct object_id *oid);
+int is_promisor_object(const struct object_id *oid);
 
 /*
  * Expose a function for fuzz testing.
@@ -176,7 +183,7 @@
  * have a convenient entry-point for fuzz testing. For real uses, you should
  * probably use open_pack_index() or parse_pack_index() instead.
  */
-extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
-		    size_t idx_size, struct packed_git *p);
+int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+	     size_t idx_size, struct packed_git *p);
 
 #endif
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 2733393..6e2e8d6 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -22,8 +22,8 @@
 				     opt->long_name);
 		if (v && v < MINIMUM_ABBREV)
 			v = MINIMUM_ABBREV;
-		else if (v > 40)
-			v = 40;
+		else if (v > the_hash_algo->hexsz)
+			v = the_hash_algo->hexsz;
 	}
 	*(int *)(opt->value) = v;
 	return 0;
diff --git a/parse-options.c b/parse-options.c
index acc3a93..cb24f1a 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -288,6 +288,8 @@
 					     optname(options, flags));
 			if (*rest)
 				continue;
+			if (options->value)
+				*(int *)options->value = options->defval;
 			p->out[p->cpidx++] = arg - 2;
 			return PARSE_OPT_DONE;
 		}
@@ -529,8 +531,7 @@
 	}
 }
 
-static int show_gitcomp(struct parse_opt_ctx_t *ctx,
-			const struct option *opts)
+static int show_gitcomp(const struct option *opts)
 {
 	const struct option *original_opts = opts;
 	int nr_noopts = 0;
@@ -609,7 +610,7 @@
 
 		/* lone --git-completion-helper is asked by git-completion.bash */
 		if (ctx->total == 1 && !strcmp(arg + 1, "-git-completion-helper"))
-			return show_gitcomp(ctx, options);
+			return show_gitcomp(options);
 
 		if (arg[1] != '-') {
 			ctx->opt = arg + 1;
diff --git a/parse-options.h b/parse-options.h
index 74cce4e..1dd3523 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -136,10 +136,12 @@
 #define OPT_BOOL_F(s, l, v, h, f)   OPT_SET_INT_F(s, l, v, h, 1, f)
 #define OPT_CALLBACK_F(s, l, v, a, h, f, cb)			\
 	{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
+#define OPT_STRING_F(s, l, v, a, h, f)   { OPTION_STRING,  (s), (l), (v), (a), (h), (f) }
+#define OPT_INTEGER_F(s, l, v, h, f)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
 
 #define OPT_END()                   { OPTION_END }
-#define OPT_ARGUMENT(l, h)          { OPTION_ARGUMENT, 0, (l), NULL, NULL, \
-				      (h), PARSE_OPT_NOARG}
+#define OPT_ARGUMENT(l, v, h)       { OPTION_ARGUMENT, 0, (l), (v), NULL, \
+				      (h), PARSE_OPT_NOARG, NULL, 1 }
 #define OPT_GROUP(h)                { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
 #define OPT_BIT(s, l, v, h, b)      OPT_BIT_F(s, l, v, h, b, 0)
 #define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
@@ -154,10 +156,10 @@
 				      (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
 #define OPT_CMDMODE(s, l, v, h, i)  { OPTION_CMDMODE, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, (i) }
-#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
+#define OPT_INTEGER(s, l, v, h)     OPT_INTEGER_F(s, l, v, h, 0)
 #define OPT_MAGNITUDE(s, l, v, h)   { OPTION_MAGNITUDE, (s), (l), (v), \
 				      N_("n"), (h), PARSE_OPT_NONEG }
-#define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
+#define OPT_STRING(s, l, v, a, h)   OPT_STRING_F(s, l, v, a, h, 0)
 #define OPT_STRING_LIST(s, l, v, a, h) \
 				    { OPTION_CALLBACK, (s), (l), (v), (a), \
 				      (h), 0, &parse_opt_string_list }
@@ -314,5 +316,6 @@
 #define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
+#define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
 
 #endif
diff --git a/path.h b/path.h
index 651e615..2ba6ca5 100644
--- a/path.h
+++ b/path.h
@@ -13,19 +13,19 @@
 /*
  * Return a statically allocated path.
  */
-extern const char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
  * Return a path.
  */
-extern char *mkpathdup(const char *fmt, ...)
+char *mkpathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
  * Construct a path and place the result in the provided buffer `buf`.
  */
-extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+char *mksnpath(char *buf, size_t n, const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
 /*
@@ -37,16 +37,16 @@
  * Constructs a path into the common git directory of repository `repo` and
  * append it in the provided buffer `sb`.
  */
-extern void strbuf_git_common_path(struct strbuf *sb,
-				   const struct repository *repo,
-				   const char *fmt, ...)
+void strbuf_git_common_path(struct strbuf *sb,
+			    const struct repository *repo,
+			    const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) common git directory.
  */
-extern const char *git_common_path(const char *fmt, ...)
+const char *git_common_path(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 
@@ -66,30 +66,30 @@
 /*
  * Return a path into the git directory of repository `repo`.
  */
-extern char *repo_git_path(const struct repository *repo,
-			   const char *fmt, ...)
+char *repo_git_path(const struct repository *repo,
+		    const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
  * Construct a path into the git directory of repository `repo` and append it
  * to the provided buffer `sb`.
  */
-extern void strbuf_repo_git_path(struct strbuf *sb,
-				 const struct repository *repo,
-				 const char *fmt, ...)
+void strbuf_repo_git_path(struct strbuf *sb,
+			  const struct repository *repo,
+			  const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
 /*
  * Return a statically allocated path into the main repository's
  * (the_repository) git directory.
  */
-extern const char *git_path(const char *fmt, ...)
+const char *git_path(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
  * Return a path into the main repository's (the_repository) git directory.
  */
-extern char *git_pathdup(const char *fmt, ...)
+char *git_pathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /*
@@ -97,14 +97,14 @@
  * and place it in the provided buffer `buf`, the contents of the buffer will
  * be overridden.
  */
-extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
  * Construct a path into the main repository's (the_repository) git directory
  * and append it to the provided buffer `sb`.
  */
-extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
@@ -112,7 +112,7 @@
  *
  * If the repository doesn't have a worktree NULL is returned.
  */
-extern char *repo_worktree_path(const struct repository *repo,
+char *repo_worktree_path(const struct repository *repo,
 				const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
@@ -122,7 +122,7 @@
  *
  * If the repository doesn't have a worktree nothing will be appended to `sb`.
  */
-extern void strbuf_repo_worktree_path(struct strbuf *sb,
+void strbuf_repo_worktree_path(struct strbuf *sb,
 				      const struct repository *repo,
 				      const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
@@ -131,7 +131,7 @@
  * Return a path into a submodule's git directory located at `path`.  `path`
  * must only reference a submodule of the main repository (the_repository).
  */
-extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
@@ -139,11 +139,11 @@
  * append it to the provided buffer `sb`.  `path` must only reference a
  * submodule of the main repository (the_repository).
  */
-extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
+int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 				     const char *fmt, ...)
 	__attribute__((format (printf, 3, 4)));
 
-extern void report_linked_checkout_garbage(void);
+void report_linked_checkout_garbage(void);
 
 /*
  * You can define a static memoized git path like:
diff --git a/perl/Git.pm b/perl/Git.pm
index d856930..62c472e 100644
--- a/perl/Git.pm
+++ b/perl/Git.pm
@@ -980,7 +980,7 @@
 		return -1;
 	}
 
-	if ($description !~ /^[0-9a-fA-F]{40} \S+ (\d+)$/) {
+	if ($description !~ /^[0-9a-fA-F]{40}(?:[0-9a-fA-F]{24})? \S+ (\d+)$/) {
 		carp "Unexpected result returned from git cat-file";
 		return -1;
 	}
diff --git a/pkt-line.c b/pkt-line.c
index ffd7220..c9ed780 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -350,16 +350,17 @@
 		return PACKET_READ_EOF;
 	}
 
-	if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
-	    starts_with(buffer, "ERR "))
-		die(_("remote error: %s"), buffer + 4);
-
 	if ((options & PACKET_READ_CHOMP_NEWLINE) &&
 	    len && buffer[len-1] == '\n')
 		len--;
 
 	buffer[len] = 0;
 	packet_trace(buffer, len, 0);
+
+	if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
+	    starts_with(buffer, "ERR "))
+		die(_("remote error: %s"), buffer + 4);
+
 	*pktlen = len;
 	return PACKET_READ_NORMAL;
 }
diff --git a/pkt-line.h b/pkt-line.h
index c36cb78..5c62015 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -172,9 +172,9 @@
  * Initialize a 'struct packet_reader' object which is an
  * abstraction around the 'packet_read_with_status()' function.
  */
-extern void packet_reader_init(struct packet_reader *reader, int fd,
-			       char *src_buffer, size_t src_len,
-			       int options);
+void packet_reader_init(struct packet_reader *reader, int fd,
+			char *src_buffer, size_t src_len,
+			int options);
 
 /*
  * Perform a packet read and return the status of the read.
@@ -186,7 +186,7 @@
  *		       'line' is set to point at the read line
  * PACKET_READ_FLUSH: 'pktlen' is set to '0' and 'line' is set to NULL
  */
-extern enum packet_read_status packet_reader_read(struct packet_reader *reader);
+enum packet_read_status packet_reader_read(struct packet_reader *reader);
 
 /*
  * Peek the next packet line without consuming it and return the status.
@@ -196,7 +196,7 @@
  * Peeking multiple times without calling 'packet_reader_read()' will return
  * the same result.
  */
-extern enum packet_read_status packet_reader_peek(struct packet_reader *reader);
+enum packet_read_status packet_reader_peek(struct packet_reader *reader);
 
 #define DEFAULT_PACKET_MAX 1000
 #define LARGE_PACKET_MAX 65520
diff --git a/ppc/sha1.c b/ppc/sha1.c
index ec6a192..1b705ce 100644
--- a/ppc/sha1.c
+++ b/ppc/sha1.c
@@ -10,8 +10,8 @@
 #include <string.h>
 #include "sha1.h"
 
-extern void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
-			  unsigned int nblocks);
+void ppc_sha1_core(uint32_t *hash, const unsigned char *p,
+		   unsigned int nblocks);
 
 int ppc_SHA1_Init(ppc_SHA_CTX *c)
 {
diff --git a/pretty.c b/pretty.c
index f496f0f..ced0485 100644
--- a/pretty.c
+++ b/pretty.c
@@ -343,8 +343,7 @@
 	return !(isalnum(ch) || ch == '!' || ch == '*' || ch == '+' || ch == '-' || ch == '/');
 }
 
-static int needs_rfc2047_encoding(const char *line, int len,
-				  enum rfc2047_type type)
+static int needs_rfc2047_encoding(const char *line, int len)
 {
 	int i;
 
@@ -470,7 +469,7 @@
 		}
 
 		strbuf_addstr(sb, "From: ");
-		if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
+		if (needs_rfc2047_encoding(namebuf, namelen)) {
 			add_rfc2047(sb, namebuf, namelen,
 				    encoding, RFC2047_ADDRESS);
 			max_length = 76; /* per rfc2047 */
@@ -989,8 +988,7 @@
 	return rest - placeholder;
 }
 
-static size_t parse_padding_placeholder(struct strbuf *sb,
-					const char *placeholder,
+static size_t parse_padding_placeholder(const char *placeholder,
 					struct format_commit_context *c)
 {
 	const char *ch = placeholder;
@@ -1195,7 +1193,7 @@
 
 	case '<':
 	case '>':
-		return parse_padding_placeholder(sb, placeholder, c);
+		return parse_padding_placeholder(placeholder, c);
 	}
 
 	/* these depend on the commit */
@@ -1728,7 +1726,7 @@
 	if (pp->print_email_subject) {
 		if (pp->rev)
 			fmt_output_email_subject(sb, pp->rev);
-		if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT))
+		if (needs_rfc2047_encoding(title.buf, title.len))
 			add_rfc2047(sb, title.buf, title.len,
 						encoding, RFC2047_SUBJECT);
 		else
diff --git a/prio-queue.h b/prio-queue.h
index 682e518..4f9a37e 100644
--- a/prio-queue.h
+++ b/prio-queue.h
@@ -37,24 +37,24 @@
 /*
  * Add the "thing" to the queue.
  */
-extern void prio_queue_put(struct prio_queue *, void *thing);
+void prio_queue_put(struct prio_queue *, void *thing);
 
 /*
  * Extract the "thing" that compares the smallest out of the queue,
  * or NULL.  If compare function is NULL, the queue acts as a LIFO
  * stack.
  */
-extern void *prio_queue_get(struct prio_queue *);
+void *prio_queue_get(struct prio_queue *);
 
 /*
  * Gain access to the "thing" that would be returned by
  * prio_queue_get, but do not remove it from the queue.
  */
-extern void *prio_queue_peek(struct prio_queue *);
+void *prio_queue_peek(struct prio_queue *);
 
-extern void clear_prio_queue(struct prio_queue *);
+void clear_prio_queue(struct prio_queue *);
 
 /* Reverse the LIFO elements */
-extern void prio_queue_reverse(struct prio_queue *);
+void prio_queue_reverse(struct prio_queue *);
 
 #endif /* PRIO_QUEUE_H */
diff --git a/progress.c b/progress.c
index 212d00e..0318bdd 100644
--- a/progress.c
+++ b/progress.c
@@ -8,11 +8,12 @@
  * published by the Free Software Foundation.
  */
 
-#include "git-compat-util.h"
+#include "cache.h"
 #include "gettext.h"
 #include "progress.h"
 #include "strbuf.h"
 #include "trace.h"
+#include "utf8.h"
 
 #define TP_IDX_MAX      8
 
@@ -37,6 +38,9 @@
 	unsigned sparse;
 	struct throughput *throughput;
 	uint64_t start_ns;
+	struct strbuf counters_sb;
+	int title_len;
+	int split;
 };
 
 static volatile sig_atomic_t progress_update;
@@ -79,41 +83,64 @@
 	return tpgrp < 0 || tpgrp == getpgid(0);
 }
 
-static int display(struct progress *progress, uint64_t n, const char *done)
+static void display(struct progress *progress, uint64_t n, const char *done)
 {
-	const char *eol, *tp;
+	const char *tp;
+	struct strbuf *counters_sb = &progress->counters_sb;
+	int show_update = 0;
+	int last_count_len = counters_sb->len;
 
 	if (progress->delay && (!progress_update || --progress->delay))
-		return 0;
+		return;
 
 	progress->last_value = n;
 	tp = (progress->throughput) ? progress->throughput->display.buf : "";
-	eol = done ? done : "   \r";
 	if (progress->total) {
 		unsigned percent = n * 100 / progress->total;
 		if (percent != progress->last_percent || progress_update) {
 			progress->last_percent = percent;
-			if (is_foreground_fd(fileno(stderr)) || done) {
-				fprintf(stderr, "%s: %3u%% (%"PRIuMAX"/%"PRIuMAX")%s%s",
-					progress->title, percent,
-					(uintmax_t)n, (uintmax_t)progress->total,
-					tp, eol);
-				fflush(stderr);
-			}
-			progress_update = 0;
-			return 1;
+
+			strbuf_reset(counters_sb);
+			strbuf_addf(counters_sb,
+				    "%3u%% (%"PRIuMAX"/%"PRIuMAX")%s", percent,
+				    (uintmax_t)n, (uintmax_t)progress->total,
+				    tp);
+			show_update = 1;
 		}
 	} else if (progress_update) {
+		strbuf_reset(counters_sb);
+		strbuf_addf(counters_sb, "%"PRIuMAX"%s", (uintmax_t)n, tp);
+		show_update = 1;
+	}
+
+	if (show_update) {
 		if (is_foreground_fd(fileno(stderr)) || done) {
-			fprintf(stderr, "%s: %"PRIuMAX"%s%s",
-				progress->title, (uintmax_t)n, tp, eol);
+			const char *eol = done ? done : "\r";
+			size_t clear_len = counters_sb->len < last_count_len ?
+					last_count_len - counters_sb->len + 1 :
+					0;
+			size_t progress_line_len = progress->title_len +
+						counters_sb->len + 2;
+			int cols = term_columns();
+
+			if (progress->split) {
+				fprintf(stderr, "  %s%*s", counters_sb->buf,
+					(int) clear_len, eol);
+			} else if (!done && cols < progress_line_len) {
+				clear_len = progress->title_len + 1 < cols ?
+					    cols - progress->title_len : 0;
+				fprintf(stderr, "%s:%*s\n  %s%s",
+					progress->title, (int) clear_len, "",
+					counters_sb->buf, eol);
+				progress->split = 1;
+			} else {
+				fprintf(stderr, "%s: %s%*s", progress->title,
+					counters_sb->buf, (int) clear_len, eol);
+			}
 			fflush(stderr);
 		}
 		progress_update = 0;
-		return 1;
 	}
-
-	return 0;
 }
 
 static void throughput_string(struct strbuf *buf, uint64_t total,
@@ -140,12 +167,10 @@
 	now_ns = getnanotime();
 
 	if (!tp) {
-		progress->throughput = tp = calloc(1, sizeof(*tp));
-		if (tp) {
-			tp->prev_total = tp->curr_total = total;
-			tp->prev_ns = now_ns;
-			strbuf_init(&tp->display, 0);
-		}
+		progress->throughput = tp = xcalloc(1, sizeof(*tp));
+		tp->prev_total = tp->curr_total = total;
+		tp->prev_ns = now_ns;
+		strbuf_init(&tp->display, 0);
 		return;
 	}
 	tp->curr_total = total;
@@ -189,21 +214,16 @@
 		display(progress, progress->last_value, NULL);
 }
 
-int display_progress(struct progress *progress, uint64_t n)
+void display_progress(struct progress *progress, uint64_t n)
 {
-	return progress ? display(progress, n, NULL) : 0;
+	if (progress)
+		display(progress, n, NULL);
 }
 
 static struct progress *start_progress_delay(const char *title, uint64_t total,
 					     unsigned delay, unsigned sparse)
 {
-	struct progress *progress = malloc(sizeof(*progress));
-	if (!progress) {
-		/* unlikely, but here's a good fallback */
-		fprintf(stderr, "%s...\n", title);
-		fflush(stderr);
-		return NULL;
-	}
+	struct progress *progress = xmalloc(sizeof(*progress));
 	progress->title = title;
 	progress->total = total;
 	progress->last_value = -1;
@@ -212,6 +232,9 @@
 	progress->sparse = sparse;
 	progress->throughput = NULL;
 	progress->start_ns = getnanotime();
+	strbuf_init(&progress->counters_sb, 0);
+	progress->title_len = utf8_strwidth(title);
+	progress->split = 0;
 	set_progress_signal();
 	return progress;
 }
@@ -285,6 +308,7 @@
 		free(buf);
 	}
 	clear_progress_signal();
+	strbuf_release(&progress->counters_sb);
 	if (progress->throughput)
 		strbuf_release(&progress->throughput->display);
 	free(progress->throughput);
diff --git a/progress.h b/progress.h
index 7b725ac..8473389 100644
--- a/progress.h
+++ b/progress.h
@@ -4,7 +4,7 @@
 struct progress;
 
 void display_throughput(struct progress *progress, uint64_t total);
-int display_progress(struct progress *progress, uint64_t n);
+void display_progress(struct progress *progress, uint64_t n);
 struct progress *start_progress(const char *title, uint64_t total);
 struct progress *start_sparse_progress(const char *title, uint64_t total);
 struct progress *start_delayed_progress(const char *title, uint64_t total);
diff --git a/protocol.h b/protocol.h
index 2ad35e4..cef1a4a 100644
--- a/protocol.h
+++ b/protocol.h
@@ -14,7 +14,7 @@
  * 'protocol.version' config.  If unconfigured, a value of 'protocol_v0' is
  * returned.
  */
-extern enum protocol_version get_protocol_version_config(void);
+enum protocol_version get_protocol_version_config(void);
 
 /*
  * Used by a server to determine which protocol version should be used based on
@@ -23,12 +23,12 @@
  * request a particular protocol version, a default of 'protocol_v0' will be
  * used.
  */
-extern enum protocol_version determine_protocol_version_server(void);
+enum protocol_version determine_protocol_version_server(void);
 
 /*
  * Used by a client to determine which protocol version the server is speaking
  * based on the server's initial response.
  */
-extern enum protocol_version determine_protocol_version_client(const char *server_response);
+enum protocol_version determine_protocol_version_client(const char *server_response);
 
 #endif /* PROTOCOL_H */
diff --git a/quote.h b/quote.h
index ea992dc..fb08dc0 100644
--- a/quote.h
+++ b/quote.h
@@ -29,9 +29,9 @@
  * sq_quotef() quotes the entire formatted string as a single result.
  */
 
-extern void sq_quote_buf(struct strbuf *, const char *src);
-extern void sq_quote_argv(struct strbuf *, const char **argv);
-extern void sq_quotef(struct strbuf *, const char *fmt, ...);
+void sq_quote_buf(struct strbuf *, const char *src);
+void sq_quote_argv(struct strbuf *, const char **argv);
+void sq_quotef(struct strbuf *, const char *fmt, ...);
 
 /*
  * These match their non-pretty variants, except that they avoid
@@ -45,14 +45,14 @@
  * NULL if the input does not look like what sq_quote would have
  * produced.
  */
-extern char *sq_dequote(char *);
+char *sq_dequote(char *);
 
 /*
  * Same as the above, but can be used to unwrap many arguments in the
  * same string separated by space. Like sq_quote, it works in place,
  * modifying arg and appending pointers into it to argv.
  */
-extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
+int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
 
 /*
  * Same as above, but store the unquoted strings in an argv_array. We will
@@ -60,24 +60,24 @@
  * will duplicate and take ownership of the strings.
  */
 struct argv_array;
-extern int sq_dequote_to_argv_array(char *arg, struct argv_array *);
+int sq_dequote_to_argv_array(char *arg, struct argv_array *);
 
-extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
-extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
-extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
+int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
+size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
+void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 
-extern void write_name_quoted(const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, const char *prefix,
-		FILE *fp, int terminator);
+void write_name_quoted(const char *name, FILE *, int terminator);
+void write_name_quoted_relative(const char *name, const char *prefix,
+				FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, const char *prefix,
+char *quote_path_relative(const char *in, const char *prefix,
 			  struct strbuf *out);
 
 /* quoting as a string literal for other languages */
-extern void perl_quote_buf(struct strbuf *sb, const char *src);
-extern void python_quote_buf(struct strbuf *sb, const char *src);
-extern void tcl_quote_buf(struct strbuf *sb, const char *src);
-extern void basic_regex_quote_buf(struct strbuf *sb, const char *src);
+void perl_quote_buf(struct strbuf *sb, const char *src);
+void python_quote_buf(struct strbuf *sb, const char *src);
+void tcl_quote_buf(struct strbuf *sb, const char *src);
+void basic_regex_quote_buf(struct strbuf *sb, const char *src);
 
 #endif
diff --git a/reachable.h b/reachable.h
index 18b0f9f..5df932a 100644
--- a/reachable.h
+++ b/reachable.h
@@ -4,9 +4,9 @@
 struct progress;
 struct rev_info;
 
-extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  timestamp_t timestamp);
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   timestamp_t mark_recent, struct progress *);
+int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
+					   timestamp_t timestamp);
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+			    timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/read-cache.c b/read-cache.c
index 4dc6de1..61b043b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -17,6 +17,7 @@
 #include "commit.h"
 #include "blob.h"
 #include "resolve-undo.h"
+#include "run-command.h"
 #include "strbuf.h"
 #include "varint.h"
 #include "split-index.h"
@@ -708,6 +709,7 @@
 	int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
 			  (intent_only ? ADD_CACHE_NEW_ONLY : 0));
 	int hash_flags = HASH_WRITE_OBJECT;
+	struct object_id oid;
 
 	if (flags & ADD_CACHE_RENORMALIZE)
 		hash_flags |= HASH_RENORMALIZE;
@@ -717,6 +719,8 @@
 
 	namelen = strlen(path);
 	if (S_ISDIR(st_mode)) {
+		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			return error(_("'%s' does not have a commit checked out"), path);
 		while (namelen && path[namelen-1] == '/')
 			namelen--;
 	}
@@ -1640,39 +1644,24 @@
 	uint32_t uid;
 	uint32_t gid;
 	uint32_t size;
-	unsigned char sha1[20];
-	uint16_t flags;
-	char name[FLEX_ARRAY]; /* more */
-};
-
-/*
- * This struct is used when CE_EXTENDED bit is 1
- * The struct must match ondisk_cache_entry exactly from
- * ctime till flags
- */
-struct ondisk_cache_entry_extended {
-	struct cache_time ctime;
-	struct cache_time mtime;
-	uint32_t dev;
-	uint32_t ino;
-	uint32_t mode;
-	uint32_t uid;
-	uint32_t gid;
-	uint32_t size;
-	unsigned char sha1[20];
-	uint16_t flags;
-	uint16_t flags2;
-	char name[FLEX_ARRAY]; /* more */
+	/*
+	 * unsigned char hash[hashsz];
+	 * uint16_t flags;
+	 * if (flags & CE_EXTENDED)
+	 *	uint16_t flags2;
+	 */
+	unsigned char data[GIT_MAX_RAWSZ + 2 * sizeof(uint16_t)];
+	char name[FLEX_ARRAY];
 };
 
 /* These are only used for v3 or lower */
 #define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
-#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,data) + (len) + 8) & ~7)
 #define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
-#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
-#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
-			    ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
-			    ondisk_cache_entry_size(ce_namelen(ce)))
+#define ondisk_data_size(flags, len) (the_hash_algo->rawsz + \
+				     ((flags & CE_EXTENDED) ? 2 : 1) * sizeof(uint16_t) + len)
+#define ondisk_data_size_max(len) (ondisk_data_size(CE_EXTENDED, len))
+#define ondisk_ce_size(ce) (ondisk_cache_entry_size(ondisk_data_size((ce)->ce_flags, ce_namelen(ce))))
 
 /* Allow fsck to force verification of the index checksum. */
 int verify_index_checksum;
@@ -1746,6 +1735,8 @@
 	struct cache_entry *ce;
 	size_t len;
 	const char *name;
+	const unsigned hashsz = the_hash_algo->rawsz;
+	const uint16_t *flagsp = (const uint16_t *)(ondisk->data + hashsz);
 	unsigned int flags;
 	size_t copy_len = 0;
 	/*
@@ -1758,22 +1749,20 @@
 	int expand_name_field = version == 4;
 
 	/* On-disk flags are just 16 bits */
-	flags = get_be16(&ondisk->flags);
+	flags = get_be16(flagsp);
 	len = flags & CE_NAMEMASK;
 
 	if (flags & CE_EXTENDED) {
-		struct ondisk_cache_entry_extended *ondisk2;
 		int extended_flags;
-		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
-		extended_flags = get_be16(&ondisk2->flags2) << 16;
+		extended_flags = get_be16(flagsp + 1) << 16;
 		/* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
 		if (extended_flags & ~CE_EXTENDED_FLAGS)
 			die(_("unknown index entry format 0x%08x"), extended_flags);
 		flags |= extended_flags;
-		name = ondisk2->name;
+		name = (const char *)(flagsp + 2);
 	}
 	else
-		name = ondisk->name;
+		name = (const char *)(flagsp + 1);
 
 	if (expand_name_field) {
 		const unsigned char *cp = (const unsigned char *)name;
@@ -1812,7 +1801,9 @@
 	ce->ce_flags = flags & ~CE_NAMEMASK;
 	ce->ce_namelen = len;
 	ce->index = 0;
-	hashcpy(ce->oid.hash, ondisk->sha1);
+	hashcpy(ce->oid.hash, ondisk->data);
+	memcpy(ce->name, name, len);
+	ce->name[len] = '\0';
 
 	if (expand_name_field) {
 		if (copy_len)
@@ -2556,6 +2547,8 @@
 				       struct cache_entry *ce)
 {
 	short flags;
+	const unsigned hashsz = the_hash_algo->rawsz;
+	uint16_t *flagsp = (uint16_t *)(ondisk->data + hashsz);
 
 	ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
 	ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
@@ -2567,15 +2560,13 @@
 	ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
 	ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
 	ondisk->size = htonl(ce->ce_stat_data.sd_size);
-	hashcpy(ondisk->sha1, ce->oid.hash);
+	hashcpy(ondisk->data, ce->oid.hash);
 
 	flags = ce->ce_flags & ~CE_NAMEMASK;
 	flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
-	ondisk->flags = htons(flags);
+	flagsp[0] = htons(flags);
 	if (ce->ce_flags & CE_EXTENDED) {
-		struct ondisk_cache_entry_extended *ondisk2;
-		ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
-		ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
+		flagsp[1] = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
 	}
 }
 
@@ -2594,10 +2585,7 @@
 		stripped_name = 1;
 	}
 
-	if (ce->ce_flags & CE_EXTENDED)
-		size = offsetof(struct ondisk_cache_entry_extended, name);
-	else
-		size = offsetof(struct ondisk_cache_entry, name);
+	size = offsetof(struct ondisk_cache_entry,data) + ondisk_data_size(ce->ce_flags, 0);
 
 	if (!previous_name) {
 		int len = ce_namelen(ce);
@@ -2755,7 +2743,7 @@
 	struct cache_entry **cache = istate->cache;
 	int entries = istate->cache_nr;
 	struct stat st;
-	struct ondisk_cache_entry_extended ondisk;
+	struct ondisk_cache_entry ondisk;
 	struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 	int drop_cache_tree = istate->drop_cache_tree;
 	off_t offset;
@@ -3049,8 +3037,17 @@
 	if (ret)
 		return ret;
 	if (flags & COMMIT_LOCK)
-		return commit_locked_index(lock);
-	return close_lock_file_gently(lock);
+		ret = commit_locked_index(lock);
+	else
+		ret = close_lock_file_gently(lock);
+
+	run_hook_le(NULL, "post-index-change",
+			istate->updated_workdir ? "1" : "0",
+			istate->updated_skipworktree ? "1" : "0", NULL);
+	istate->updated_workdir = 0;
+	istate->updated_skipworktree = 0;
+
+	return ret;
 }
 
 static int write_split_index(struct index_state *istate,
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 68aff1d..aa18ae8 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,10 +1,35 @@
 #include "cache.h"
 #include "commit.h"
-#include "rebase-interactive.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 #include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+enum missing_commit_check_level {
+	MISSING_COMMIT_CHECK_IGNORE = 0,
+	MISSING_COMMIT_CHECK_WARN,
+	MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+	const char *value;
+
+	if (git_config_get_value("rebase.missingcommitscheck", &value) ||
+			!strcasecmp("ignore", value))
+		return MISSING_COMMIT_CHECK_IGNORE;
+	if (!strcasecmp("warn", value))
+		return MISSING_COMMIT_CHECK_WARN;
+	if (!strcasecmp("error", value))
+		return MISSING_COMMIT_CHECK_ERROR;
+	warning(_("unrecognized setting %s for option "
+		  "rebase.missingCommitsCheck. Ignoring."), value);
+	return MISSING_COMMIT_CHECK_IGNORE;
+}
+
+void append_todo_help(unsigned keep_empty, int command_count,
+		      const char *shortrevisions, const char *shortonto,
 		      struct strbuf *buf)
 {
 	const char *msg = _("\nCommands:\n"
@@ -24,6 +49,15 @@
 ".       specified). Use -c <commit> to reword the commit message.\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
+	unsigned edit_todo = !(shortrevisions && shortonto);
+
+	if (!edit_todo) {
+		strbuf_addch(buf, '\n');
+		strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+					      "Rebase %s onto %s (%d commands)",
+					      command_count),
+				      shortrevisions, shortonto, command_count);
+	}
 
 	strbuf_add_commented_lines(buf, msg, strlen(msg));
 
@@ -53,39 +87,103 @@
 	}
 }
 
-int edit_todo_list(struct repository *r, unsigned flags)
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+		   struct todo_list *new_todo, const char *shortrevisions,
+		   const char *shortonto, unsigned flags)
 {
-	struct strbuf buf = STRBUF_INIT;
 	const char *todo_file = rebase_path_todo();
+	unsigned initial = shortrevisions && shortonto;
 
-	if (strbuf_read_file(&buf, todo_file, 0) < 0)
-		return error_errno(_("could not read '%s'."), todo_file);
+	/* If the user is editing the todo list, we first try to parse
+	 * it.  If there is an error, we do not return, because the user
+	 * might want to fix it in the first place. */
+	if (!initial)
+		todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
 
-	strbuf_stripspace(&buf, 1);
-	if (write_message(buf.buf, buf.len, todo_file, 0)) {
-		strbuf_release(&buf);
-		return -1;
-	}
+	if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
+				    -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))
+		return error_errno(_("could not write '%s'"), todo_file);
 
-	strbuf_release(&buf);
+	if (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))
+		return error(_("could not copy '%s' to '%s'."), todo_file,
+			     rebase_path_todo_backup());
 
-	transform_todos(r, flags | TODO_LIST_SHORTEN_IDS);
+	if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
+		return -2;
 
-	if (strbuf_read_file(&buf, todo_file, 0) < 0)
-		return error_errno(_("could not read '%s'."), todo_file);
+	strbuf_stripspace(&new_todo->buf, 1);
+	if (initial && new_todo->buf.len == 0)
+		return -3;
 
-	append_todo_help(1, 0, &buf);
-	if (write_message(buf.buf, buf.len, todo_file, 0)) {
-		strbuf_release(&buf);
-		return -1;
-	}
-
-	strbuf_release(&buf);
-
-	if (launch_sequence_editor(todo_file, NULL, NULL))
-		return -1;
-
-	transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));
+	/* For the initial edit, the todo list gets parsed in
+	 * complete_action(). */
+	if (!initial)
+		return todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);
 
 	return 0;
 }
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+	enum missing_commit_check_level check_level = get_missing_commit_check_level();
+	struct strbuf missing = STRBUF_INIT;
+	int res = 0, i;
+	struct commit_seen commit_seen;
+
+	init_commit_seen(&commit_seen);
+
+	if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+		goto leave_check;
+
+	/* Mark the commits in git-rebase-todo as seen */
+	for (i = 0; i < new_todo->nr; i++) {
+		struct commit *commit = new_todo->items[i].commit;
+		if (commit)
+			*commit_seen_at(&commit_seen, commit) = 1;
+	}
+
+	/* Find commits in git-rebase-todo.backup yet unseen */
+	for (i = old_todo->nr - 1; i >= 0; i--) {
+		struct todo_item *item = old_todo->items + i;
+		struct commit *commit = item->commit;
+		if (commit && !*commit_seen_at(&commit_seen, commit)) {
+			strbuf_addf(&missing, " - %s %.*s\n",
+				    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
+				    item->arg_len,
+				    todo_item_get_arg(old_todo, item));
+			*commit_seen_at(&commit_seen, commit) = 1;
+		}
+	}
+
+	/* Warn about missing commits */
+	if (!missing.len)
+		goto leave_check;
+
+	if (check_level == MISSING_COMMIT_CHECK_ERROR)
+		res = 1;
+
+	fprintf(stderr,
+		_("Warning: some commits may have been dropped accidentally.\n"
+		"Dropped commits (newer to older):\n"));
+
+	/* Make the list user-friendly and display */
+	fputs(missing.buf, stderr);
+	strbuf_release(&missing);
+
+	fprintf(stderr, _("To avoid this message, use \"drop\" to "
+		"explicitly remove a commit.\n\n"
+		"Use 'git config rebase.missingCommitsCheck' to change "
+		"the level of warnings.\n"
+		"The possible behaviours are: ignore, warn, error.\n\n"));
+
+leave_check:
+	clear_commit_seen(&commit_seen);
+	return res;
+}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 17b6c9f..44dbb06 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -3,9 +3,14 @@
 
 struct strbuf;
 struct repository;
+struct todo_list;
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+		      const char *shortrevisions, const char *shortonto,
 		      struct strbuf *buf);
-int edit_todo_list(struct repository *r, unsigned flags);
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+		   struct todo_list *new_todo, const char *shortrevisions,
+		   const char *shortonto, unsigned flags);
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index 3aca105..8500671 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1392,7 +1392,8 @@
 		*s = show_ref(&atom->u.remote_ref.refname, refname);
 	else if (atom->u.remote_ref.option == RR_TRACK) {
 		if (stat_tracking_info(branch, &num_ours, &num_theirs,
-				       NULL, AHEAD_BEHIND_FULL) < 0) {
+				       NULL, atom->u.remote_ref.push,
+				       AHEAD_BEHIND_FULL) < 0) {
 			*s = xstrdup(msgs.gone);
 		} else if (!num_ours && !num_theirs)
 			*s = xstrdup("");
@@ -1410,7 +1411,8 @@
 		}
 	} else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
 		if (stat_tracking_info(branch, &num_ours, &num_theirs,
-				       NULL, AHEAD_BEHIND_FULL) < 0) {
+				       NULL, atom->u.remote_ref.push,
+				       AHEAD_BEHIND_FULL) < 0) {
 			*s = xstrdup("");
 			return;
 		}
@@ -2337,8 +2339,13 @@
 
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
 {
-	if (!arg) /* should --no-sort void the list ? */
-		return -1;
+	/*
+	 * NEEDSWORK: We should probably clear the list in this case, but we've
+	 * already munged the global used_atoms list, which would need to be
+	 * undone.
+	 */
+	BUG_ON_OPT_NEG(unset);
+
 	parse_ref_sorting(opt->value, arg);
 	return 0;
 }
diff --git a/ref-filter.h b/ref-filter.h
index 85c8ebc..f1dcff4 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -96,6 +96,11 @@
 #define OPT_MERGED(f, h) _OPT_MERGED_NO_MERGED("merged", f, h)
 #define OPT_NO_MERGED(f, h) _OPT_MERGED_NO_MERGED("no-merged", f, h)
 
+#define OPT_REF_SORT(var) \
+	OPT_CALLBACK_F(0, "sort", (var), \
+		       N_("key"), N_("field name to sort on"), \
+		       PARSE_OPT_NONEG, parse_opt_ref_sorting)
+
 /*
  * API for filtering a set of refs. Based on the type of refs the user
  * has requested, we iterate through those refs and apply filters
diff --git a/reflog-walk.h b/reflog-walk.h
index cb3e737..f26408f 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -6,21 +6,21 @@
 struct commit;
 struct reflog_walk_info;
 
-extern void init_reflog_walk(struct reflog_walk_info **info);
-extern int add_reflog_for_walk(struct reflog_walk_info *info,
-		struct commit *commit, const char *name);
-extern void show_reflog_message(struct reflog_walk_info *info, int,
-				const struct date_mode *, int force_date);
-extern void get_reflog_message(struct strbuf *sb,
-		struct reflog_walk_info *reflog_info);
-extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
-extern timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
-extern void get_reflog_selector(struct strbuf *sb,
-		struct reflog_walk_info *reflog_info,
-		const struct date_mode *dmode, int force_date,
-		int shorten);
+void init_reflog_walk(struct reflog_walk_info **info);
+int add_reflog_for_walk(struct reflog_walk_info *info,
+			struct commit *commit, const char *name);
+void show_reflog_message(struct reflog_walk_info *info, int,
+			 const struct date_mode *, int force_date);
+void get_reflog_message(struct strbuf *sb,
+			struct reflog_walk_info *reflog_info);
+const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
+timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
+void get_reflog_selector(struct strbuf *sb,
+			 struct reflog_walk_info *reflog_info,
+			 const struct date_mode *dmode, int force_date,
+			 int shorten);
 
-extern int reflog_walk_empty(struct reflog_walk_info *walk);
+int reflog_walk_empty(struct reflog_walk_info *walk);
 
 struct commit *next_reflog_entry(struct reflog_walk_info *reflog_info);
 
diff --git a/refs.c b/refs.c
index 142888a..92d1f6d 100644
--- a/refs.c
+++ b/refs.c
@@ -241,9 +241,14 @@
 	return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
 }
 
+static int refs_ref_exists(struct ref_store *refs, const char *refname)
+{
+	return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL);
+}
+
 int ref_exists(const char *refname)
 {
-	return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
+	return refs_ref_exists(get_main_ref_store(the_repository), refname);
 }
 
 static int match_ref_pattern(const char *refname,
@@ -534,10 +539,11 @@
  * later free()ing) if the string passed in is a magic short-hand form
  * to name a branch.
  */
-static char *substitute_branch_name(const char **string, int *len)
+static char *substitute_branch_name(struct repository *r,
+				    const char **string, int *len)
 {
 	struct strbuf buf = STRBUF_INIT;
-	int ret = interpret_branch_name(*string, *len, &buf, 0);
+	int ret = repo_interpret_branch_name(r, *string, *len, &buf, 0);
 
 	if (ret == *len) {
 		size_t size;
@@ -549,15 +555,22 @@
 	return NULL;
 }
 
-int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
+int repo_dwim_ref(struct repository *r, const char *str, int len,
+		  struct object_id *oid, char **ref)
 {
-	char *last_branch = substitute_branch_name(&str, &len);
-	int   refs_found  = expand_ref(str, len, oid, ref);
+	char *last_branch = substitute_branch_name(r, &str, &len);
+	int   refs_found  = expand_ref(r, str, len, oid, ref);
 	free(last_branch);
 	return refs_found;
 }
 
-int expand_ref(const char *str, int len, struct object_id *oid, char **ref)
+int dwim_ref(const char *str, int len, struct object_id *oid, char **ref)
+{
+	return repo_dwim_ref(the_repository, str, len, oid, ref);
+}
+
+int expand_ref(struct repository *repo, const char *str, int len,
+	       struct object_id *oid, char **ref)
 {
 	const char **p, *r;
 	int refs_found = 0;
@@ -572,8 +585,9 @@
 		this_result = refs_found ? &oid_from_ref : oid;
 		strbuf_reset(&fullref);
 		strbuf_addf(&fullref, *p, len, str);
-		r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
-				       this_result, &flag);
+		r = refs_resolve_ref_unsafe(get_main_ref_store(repo),
+					    fullref.buf, RESOLVE_REF_READING,
+					    this_result, &flag);
 		if (r) {
 			if (!refs_found++)
 				*ref = xstrdup(r);
@@ -589,9 +603,11 @@
 	return refs_found;
 }
 
-int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+int repo_dwim_log(struct repository *r, const char *str, int len,
+		  struct object_id *oid, char **log)
 {
-	char *last_branch = substitute_branch_name(&str, &len);
+	struct ref_store *refs = get_main_ref_store(r);
+	char *last_branch = substitute_branch_name(r, &str, &len);
 	const char **p;
 	int logs_found = 0;
 	struct strbuf path = STRBUF_INIT;
@@ -603,13 +619,15 @@
 
 		strbuf_reset(&path);
 		strbuf_addf(&path, *p, len, str);
-		ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
-					 &hash, NULL);
+		ref = refs_resolve_ref_unsafe(refs, path.buf,
+					      RESOLVE_REF_READING,
+					      &hash, NULL);
 		if (!ref)
 			continue;
-		if (reflog_exists(path.buf))
+		if (refs_reflog_exists(refs, path.buf))
 			it = path.buf;
-		else if (strcmp(ref, path.buf) && reflog_exists(ref))
+		else if (strcmp(ref, path.buf) &&
+			 refs_reflog_exists(refs, ref))
 			it = ref;
 		else
 			continue;
@@ -625,6 +643,11 @@
 	return logs_found;
 }
 
+int dwim_log(const char *str, int len, struct object_id *oid, char **log)
+{
+	return repo_dwim_log(the_repository, str, len, oid, log);
+}
+
 static int is_per_worktree_ref(const char *refname)
 {
 	return !strcmp(refname, "HEAD") ||
@@ -944,7 +967,8 @@
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
+int read_ref_at(struct ref_store *refs, const char *refname,
+		unsigned int flags, timestamp_t at_time, int cnt,
 		struct object_id *oid, char **msg,
 		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
@@ -960,7 +984,7 @@
 	cb.cutoff_cnt = cutoff_cnt;
 	cb.oid = oid;
 
-	for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
+	refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
 
 	if (!cb.reccnt) {
 		if (flags & GET_OID_QUIETLY)
@@ -971,7 +995,7 @@
 	if (cb.found_it)
 		return 0;
 
-	for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
+	refs_for_each_reflog_ent(refs, refname, read_ref_at_ent_oldest, &cb);
 
 	return 1;
 }
@@ -1164,7 +1188,8 @@
 			       old_oid, flags, onerr);
 }
 
-char *shorten_unambiguous_ref(const char *refname, int strict)
+char *refs_shorten_unambiguous_ref(struct ref_store *refs,
+				   const char *refname, int strict)
 {
 	int i;
 	static char **scanf_fmts;
@@ -1242,7 +1267,7 @@
 			strbuf_reset(&resolved_buf);
 			strbuf_addf(&resolved_buf, rule,
 				    short_name_len, short_name);
-			if (ref_exists(resolved_buf.buf))
+			if (refs_ref_exists(refs, resolved_buf.buf))
 				break;
 		}
 
@@ -1261,6 +1286,12 @@
 	return xstrdup(refname);
 }
 
+char *shorten_unambiguous_ref(const char *refname, int strict)
+{
+	return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+					    refname, strict);
+}
+
 static struct string_list *hide_refs;
 
 int parse_hide_refs_config(const char *var, const char *value, const char *section)
diff --git a/refs.h b/refs.h
index 308fa1f..2727405 100644
--- a/refs.h
+++ b/refs.h
@@ -111,7 +111,7 @@
 
 int is_branch(const char *refname);
 
-extern int refs_init_db(struct strbuf *err);
+int refs_init_db(struct strbuf *err);
 
 /*
  * If refname is a non-symbolic reference that refers to a tag object,
@@ -148,7 +148,9 @@
 struct argv_array;
 void expand_ref_prefix(struct argv_array *prefixes, const char *prefix);
 
-int expand_ref(const char *str, int len, struct object_id *oid, char **ref);
+int expand_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
+int repo_dwim_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
+int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
 int dwim_ref(const char *str, int len, struct object_id *oid, char **ref);
 int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
 
@@ -386,7 +388,8 @@
 int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
 
 /** Reads log for the value of ref during at_time. **/
-int read_ref_at(const char *refname, unsigned int flags,
+int read_ref_at(struct ref_store *refs,
+		const char *refname, unsigned int flags,
 		timestamp_t at_time, int cnt,
 		struct object_id *oid, char **msg,
 		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
@@ -462,6 +465,8 @@
 
 const char *prettify_refname(const char *refname);
 
+char *refs_shorten_unambiguous_ref(struct ref_store *refs,
+				   const char *refname, int strict);
 char *shorten_unambiguous_ref(const char *refname, int strict);
 
 /** rename ref, return 0 on success **/
diff --git a/refspec.c b/refspec.c
index f529092..9a9bf21 100644
--- a/refspec.c
+++ b/refspec.c
@@ -72,7 +72,7 @@
 		/* LHS */
 		if (!*item->src)
 			; /* empty is ok; it means "HEAD" */
-		else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused))
+		else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
 			item->exact_sha1 = 1; /* ok */
 		else if (!check_refname_format(item->src, flags))
 			; /* valid looking ref is ok */
diff --git a/remote-curl.c b/remote-curl.c
index 8bba572..a1bf4f6 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -250,7 +250,7 @@
 		if (data[i] == '\t')
 			mid = &data[i];
 		if (data[i] == '\n') {
-			if (mid - start != 40)
+			if (mid - start != the_hash_algo->hexsz)
 				die(_("%sinfo/refs not valid: is this a git repository?"),
 				    transport_anonymize_url(url.buf));
 			data[i] = 0;
@@ -1114,12 +1114,13 @@
 			const char *name;
 			struct ref *ref;
 			struct object_id old_oid;
+			const char *q;
 
-			if (get_oid_hex(p, &old_oid))
+			if (parse_oid_hex(p, &old_oid, &q))
 				die(_("protocol error: expected sha/ref, got %s'"), p);
-			if (p[GIT_SHA1_HEXSZ] == ' ')
-				name = p + GIT_SHA1_HEXSZ + 1;
-			else if (!p[GIT_SHA1_HEXSZ])
+			if (*q == ' ')
+				name = q + 1;
+			else if (!*q)
 				name = "";
 			else
 				die(_("protocol error: expected sha/ref, got %s'"), p);
diff --git a/remote.c b/remote.c
index 9cc3b07..e50f760 100644
--- a/remote.c
+++ b/remote.c
@@ -820,11 +820,11 @@
 	return ret;
 }
 
-static void free_ref(struct ref *ref)
+void free_one_ref(struct ref *ref)
 {
 	if (!ref)
 		return;
-	free_ref(ref->peer_ref);
+	free_one_ref(ref->peer_ref);
 	free(ref->remote_status);
 	free(ref->symref);
 	free(ref);
@@ -835,7 +835,7 @@
 	struct ref *next;
 	while (ref) {
 		next = ref->next;
-		free_ref(ref);
+		free_one_ref(ref);
 		ref = next;
 	}
 }
@@ -1880,37 +1880,27 @@
 }
 
 /*
- * Lookup the upstream branch for the given branch and if present, optionally
- * compute the commit ahead/behind values for the pair.
+ * Compute the commit ahead/behind values for the pair branch_name, base.
  *
  * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
  * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip
  * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
  * set to zero).
  *
- * The name of the upstream branch (or NULL if no upstream is defined) is
- * returned via *upstream_name, if it is not itself NULL.
- *
- * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
- * upstream defined, or ref does not exist).  Returns 0 if the commits are
- * identical.  Returns 1 if commits are different.
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., ref
+ * does not exist).  Returns 0 if the commits are identical.  Returns 1 if
+ * commits are different.
  */
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
-		       const char **upstream_name, enum ahead_behind_flags abf)
+
+static int stat_branch_pair(const char *branch_name, const char *base,
+			     int *num_ours, int *num_theirs,
+			     enum ahead_behind_flags abf)
 {
 	struct object_id oid;
 	struct commit *ours, *theirs;
 	struct rev_info revs;
-	const char *base;
 	struct argv_array argv = ARGV_ARRAY_INIT;
 
-	/* Cannot stat unless we are marked to build on top of somebody else. */
-	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 */
 	if (read_ref(base, &oid))
 		return -1;
@@ -1918,7 +1908,7 @@
 	if (!theirs)
 		return -1;
 
-	if (read_ref(branch->refname, &oid))
+	if (read_ref(branch_name, &oid))
 		return -1;
 	ours = lookup_commit_reference(the_repository, &oid);
 	if (!ours)
@@ -1932,7 +1922,7 @@
 	if (abf == AHEAD_BEHIND_QUICK)
 		return 1;
 	if (abf != AHEAD_BEHIND_FULL)
-		BUG("stat_tracking_info: invalid abf '%d'", abf);
+		BUG("stat_branch_pair: invalid abf '%d'", abf);
 
 	/* Run "rev-list --left-right ours...theirs" internally... */
 	argv_array_push(&argv, ""); /* ignored */
@@ -1967,6 +1957,42 @@
 }
 
 /*
+ * Lookup the tracking branch for the given branch and if present, optionally
+ * compute the commit ahead/behind values for the pair.
+ *
+ * If for_push is true, the tracking branch refers to the push branch,
+ * otherwise it refers to the upstream branch.
+ *
+ * The name of the tracking branch (or NULL if it is not defined) is
+ * returned via *tracking_name, if it is not itself NULL.
+ *
+ * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the
+ * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip
+ * the (potentially expensive) a/b computation (*num_ours and *num_theirs are
+ * set to zero).
+ *
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist).  Returns 0 if the commits are
+ * identical.  Returns 1 if commits are different.
+ */
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+		       const char **tracking_name, int for_push,
+		       enum ahead_behind_flags abf)
+{
+	const char *base;
+
+	/* Cannot stat unless we are marked to build on top of somebody else. */
+	base = for_push ? branch_get_push(branch, NULL) :
+		branch_get_upstream(branch, NULL);
+	if (tracking_name)
+		*tracking_name = base;
+	if (!base)
+		return -1;
+
+	return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
+}
+
+/*
  * Return true when there is anything to report, otherwise false.
  */
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
@@ -1977,7 +2003,7 @@
 	char *base;
 	int upstream_is_gone = 0;
 
-	sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf);
+	sti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
 	if (sti < 0) {
 		if (!full_base)
 			return 0;
diff --git a/remote.h b/remote.h
index da53ad5..83e8856 100644
--- a/remote.h
+++ b/remote.h
@@ -119,36 +119,38 @@
 #define REF_HEADS	(1u << 1)
 #define REF_TAGS	(1u << 2)
 
-extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
+struct ref *find_ref_by_name(const struct ref *list, const char *name);
 
 struct ref *alloc_ref(const char *name);
 struct ref *copy_ref(const struct ref *ref);
 struct ref *copy_ref_list(const struct ref *ref);
 void sort_ref_list(struct ref **, int (*cmp)(const void *, const void *));
-extern int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
+int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref);
 int ref_compare_name(const void *, const void *);
 
 int check_ref_type(const struct ref *ref, int flags);
 
 /*
- * Frees the entire list and peers of elements.
+ * Free a single ref and its peer, or an entire list of refs and their peers,
+ * respectively.
  */
+void free_one_ref(struct ref *ref);
 void free_refs(struct ref *ref);
 
 struct oid_array;
 struct packet_reader;
 struct argv_array;
 struct string_list;
-extern struct ref **get_remote_heads(struct packet_reader *reader,
-				     struct ref **list, unsigned int flags,
-				     struct oid_array *extra_have,
-				     struct oid_array *shallow_points);
+struct ref **get_remote_heads(struct packet_reader *reader,
+			      struct ref **list, unsigned int flags,
+			      struct oid_array *extra_have,
+			      struct oid_array *shallow_points);
 
 /* Used for protocol v2 in order to retrieve refs from a remote */
-extern struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
-				    struct ref **list, int for_push,
-				    const struct argv_array *ref_prefixes,
-				    const struct string_list *server_options);
+struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
+			     struct ref **list, int for_push,
+			     const struct argv_array *ref_prefixes,
+			     const struct string_list *server_options);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 
@@ -253,7 +255,8 @@
 
 /* Reporting of tracking info */
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
-		       const char **upstream_name, enum ahead_behind_flags abf);
+		       const char **upstream_name, int for_push,
+		       enum ahead_behind_flags abf);
 int format_tracking_info(struct branch *branch, struct strbuf *sb,
 			 enum ahead_behind_flags abf);
 
@@ -287,9 +290,9 @@
 	int alloc;
 };
 
-extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
+int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
 
-extern int is_empty_cas(const struct push_cas_option *);
+int is_empty_cas(const struct push_cas_option *);
 void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
 
 #endif
diff --git a/replace-object.h b/replace-object.h
index 16528df..04ed7a8 100644
--- a/replace-object.h
+++ b/replace-object.h
@@ -16,8 +16,8 @@
  * This internal function is only declared here for the benefit of
  * lookup_replace_object().  Please do not call it directly.
  */
-extern const struct object_id *do_lookup_replace_object(struct repository *r,
-							const struct object_id *oid);
+const struct object_id *do_lookup_replace_object(struct repository *r,
+						 const struct object_id *oid);
 
 /*
  * If object sha1 should be replaced, return the replacement object's
diff --git a/resolve-undo.h b/resolve-undo.h
index fbe348e..2b3f0f9 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -8,12 +8,12 @@
 	struct object_id oid[3];
 };
 
-extern void record_resolve_undo(struct index_state *, struct cache_entry *);
-extern void resolve_undo_write(struct strbuf *, struct string_list *);
-extern struct string_list *resolve_undo_read(const char *, unsigned long);
-extern void resolve_undo_clear_index(struct index_state *);
-extern int unmerge_index_entry_at(struct index_state *, int);
-extern void unmerge_index(struct index_state *, const struct pathspec *);
-extern void unmerge_marked_index(struct index_state *);
+void record_resolve_undo(struct index_state *, struct cache_entry *);
+void resolve_undo_write(struct strbuf *, struct string_list *);
+struct string_list *resolve_undo_read(const char *, unsigned long);
+void resolve_undo_clear_index(struct index_state *);
+int unmerge_index_entry_at(struct index_state *, int);
+void unmerge_index(struct index_state *, const struct pathspec *);
+void unmerge_marked_index(struct index_state *);
 
 #endif
diff --git a/revision.c b/revision.c
index cb69a22..d4aaf0e 100644
--- a/revision.c
+++ b/revision.c
@@ -911,26 +911,11 @@
 		commit->object.flags |= TREESAME;
 }
 
-static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
-		    struct commit_list *cached_base, struct commit_list **cache)
-{
-	struct commit_list *new_entry;
-
-	if (cached_base && p->date < cached_base->item->date)
-		new_entry = commit_list_insert_by_date(p, &cached_base->next);
-	else
-		new_entry = commit_list_insert_by_date(p, head);
-
-	if (cache && (!*cache || p->date < (*cache)->item->date))
-		*cache = new_entry;
-}
-
 static int process_parents(struct rev_info *revs, struct commit *commit,
-			   struct commit_list **list, struct commit_list **cache_ptr)
+			   struct commit_list **list, struct prio_queue *queue)
 {
 	struct commit_list *parent = commit->parents;
 	unsigned left_flag;
-	struct commit_list *cached_base = cache_ptr ? *cache_ptr : NULL;
 
 	if (commit->object.flags & ADDED)
 		return 0;
@@ -966,7 +951,9 @@
 				continue;
 			p->object.flags |= SEEN;
 			if (list)
-				commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+				commit_list_insert_by_date(p, list);
+			if (queue)
+				prio_queue_put(queue, p);
 		}
 		return 0;
 	}
@@ -1006,7 +993,9 @@
 		if (!(p->object.flags & SEEN)) {
 			p->object.flags |= SEEN;
 			if (list)
-				commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+				commit_list_insert_by_date(p, list);
+			if (queue)
+				prio_queue_put(queue, p);
 		}
 		if (revs->first_parent_only)
 			break;
@@ -1894,7 +1883,7 @@
 	return 0;
 }
 
-static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
+static void read_pathspec_from_stdin(struct strbuf *sb,
 				     struct argv_array *prune)
 {
 	while (strbuf_getline(sb, stdin) != EOF)
@@ -1928,7 +1917,7 @@
 			die("bad revision '%s'", sb.buf);
 	}
 	if (seen_dashdash)
-		read_pathspec_from_stdin(revs, &sb, prune);
+		read_pathspec_from_stdin(&sb, prune);
 
 	strbuf_release(&sb);
 	warn_on_object_refname_ambiguity = save_warning;
@@ -2752,7 +2741,7 @@
 	return st;
 }
 
-static int mark_redundant_parents(struct rev_info *revs, struct commit *commit)
+static int mark_redundant_parents(struct commit *commit)
 {
 	struct commit_list *h = reduce_heads(commit->parents);
 	int i = 0, marked = 0;
@@ -2788,7 +2777,7 @@
 	return marked;
 }
 
-static int mark_treesame_root_parents(struct rev_info *revs, struct commit *commit)
+static int mark_treesame_root_parents(struct commit *commit)
 {
 	struct commit_list *p;
 	int marked = 0;
@@ -2980,8 +2969,8 @@
 	 * Detect and simplify both cases.
 	 */
 	if (1 < cnt) {
-		int marked = mark_redundant_parents(revs, commit);
-		marked += mark_treesame_root_parents(revs, commit);
+		int marked = mark_redundant_parents(commit);
+		marked += mark_treesame_root_parents(commit);
 		if (marked)
 			marked -= leave_one_treesame_to_parent(revs, commit);
 		if (marked)
@@ -3345,14 +3334,14 @@
 	return 0;
 }
 
-static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
+static enum rewrite_result rewrite_one_1(struct rev_info *revs,
+					 struct commit **pp,
+					 struct prio_queue *queue)
 {
-	struct commit_list *cache = NULL;
-
 	for (;;) {
 		struct commit *p = *pp;
 		if (!revs->limited)
-			if (process_parents(revs, p, &revs->commits, &cache) < 0)
+			if (process_parents(revs, p, NULL, queue) < 0)
 				return rewrite_one_error;
 		if (p->object.flags & UNINTERESTING)
 			return rewrite_one_ok;
@@ -3366,6 +3355,31 @@
 	}
 }
 
+static void merge_queue_into_list(struct prio_queue *q, struct commit_list **list)
+{
+	while (q->nr) {
+		struct commit *item = prio_queue_peek(q);
+		struct commit_list *p = *list;
+
+		if (p && p->item->date >= item->date)
+			list = &p->next;
+		else {
+			p = commit_list_insert(item, list);
+			list = &p->next; /* skip newly added item */
+			prio_queue_get(q); /* pop item */
+		}
+	}
+}
+
+static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
+{
+	struct prio_queue queue = { compare_commits_by_commit_date };
+	enum rewrite_result ret = rewrite_one_1(revs, pp, &queue);
+	merge_queue_into_list(&queue, &revs->commits);
+	clear_prio_queue(&queue);
+	return ret;
+}
+
 int rewrite_parents(struct rev_info *revs, struct commit *commit,
 	rewrite_parent_fn_t rewrite_parent)
 {
diff --git a/run-command.h b/run-command.h
index a695069..f769e03 100644
--- a/run-command.h
+++ b/run-command.h
@@ -55,7 +55,7 @@
 #define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
 void child_process_init(struct child_process *);
 void child_process_clear(struct child_process *);
-extern int is_executable(const char *name);
+int is_executable(const char *name);
 
 int start_command(struct child_process *);
 int finish_command(struct child_process *);
@@ -67,10 +67,10 @@
  * or disabled. Note that this points to static storage that will be
  * overwritten by further calls to find_hook and run_hook_*.
  */
-extern const char *find_hook(const char *name);
+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);
+int run_hook_le(const char *const *env, const char *name, ...);
+int run_hook_ve(const char *const *env, const char *name, va_list args);
 
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD	     2	/*If this is to be git sub-command */
diff --git a/sequencer.c b/sequencer.c
index 79a046d..4407a3f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -55,8 +55,7 @@
  * file and written to the tail of 'done'.
  */
 GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
-static GIT_PATH_FUNC(rebase_path_todo_backup,
-		     "rebase-merge/git-rebase-todo.backup")
+GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
 
 /*
  * The rebase command lines that have already been processed. A line
@@ -172,17 +171,22 @@
 		if (status)
 			return status;
 
-		if (!strcmp(s, "verbatim"))
+		if (!strcmp(s, "verbatim")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
-		else if (!strcmp(s, "whitespace"))
+			opts->explicit_cleanup = 1;
+		} else if (!strcmp(s, "whitespace")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-		else if (!strcmp(s, "strip"))
+			opts->explicit_cleanup = 1;
+		} else if (!strcmp(s, "strip")) {
 			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
-		else if (!strcmp(s, "scissors"))
-			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
-		else
+			opts->explicit_cleanup = 1;
+		} else if (!strcmp(s, "scissors")) {
+			opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
+			opts->explicit_cleanup = 1;
+		} else {
 			warning(_("invalid commit message cleanup mode '%s'"),
 				  s);
+		}
 
 		free((char *)s);
 		return status;
@@ -384,8 +388,8 @@
 	}
 }
 
-int write_message(const void *buf, size_t len, const char *filename,
-		  int append_eol)
+static int write_message(const void *buf, size_t len, const char *filename,
+			 int append_eol)
 {
 	struct lock_file msg_file = LOCK_INIT;
 
@@ -511,11 +515,54 @@
 	return 0;
 }
 
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+	int use_editor)
+{
+	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+		return use_editor ? COMMIT_MSG_CLEANUP_ALL :
+				    COMMIT_MSG_CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "verbatim"))
+		return COMMIT_MSG_CLEANUP_NONE;
+	else if (!strcmp(cleanup_arg, "whitespace"))
+		return COMMIT_MSG_CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "strip"))
+		return COMMIT_MSG_CLEANUP_ALL;
+	else if (!strcmp(cleanup_arg, "scissors"))
+		return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+				    COMMIT_MSG_CLEANUP_SPACE;
+	else
+		die(_("Invalid cleanup mode %s"), cleanup_arg);
+}
+
+/*
+ * NB using int rather than enum cleanup_mode to stop clang's
+ * -Wtautological-constant-out-of-range-compare complaining that the comparison
+ * is always true.
+ */
+static const char *describe_cleanup_mode(int cleanup_mode)
+{
+	static const char *modes[] = { "whitespace",
+				       "verbatim",
+				       "scissors",
+				       "strip" };
+
+	if (cleanup_mode < ARRAY_SIZE(modes))
+		return modes[cleanup_mode];
+
+	BUG("invalid cleanup_mode provided (%d)", cleanup_mode);
+}
+
 void append_conflicts_hint(struct index_state *istate,
-			   struct strbuf *msgbuf)
+	struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)
 {
 	int i;
 
+	if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+		strbuf_addch(msgbuf, '\n');
+		wt_status_append_cut_line(msgbuf);
+		strbuf_addch(msgbuf, comment_line_char);
+	}
+
 	strbuf_addch(msgbuf, '\n');
 	strbuf_commented_addf(msgbuf, "Conflicts:\n");
 	for (i = 0; i < istate->cache_nr;) {
@@ -583,7 +630,8 @@
 			_(action_name(opts)));
 
 	if (!clean)
-		append_conflicts_hint(r->index, msgbuf);
+		append_conflicts_hint(r->index, msgbuf,
+				      opts->default_msg_cleanup);
 
 	return !clean;
 }
@@ -902,7 +950,6 @@
 			  unsigned int flags)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
-	const char *value;
 
 	if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
 		struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
@@ -972,7 +1019,7 @@
 		argv_array_push(&cmd.args, "-e");
 	else if (!(flags & CLEANUP_MSG) &&
 		 !opts->signoff && !opts->record_origin &&
-		 git_config_get_value("commit.cleanup", &value))
+		 !opts->explicit_cleanup)
 		argv_array_push(&cmd.args, "--cleanup=verbatim");
 
 	if ((flags & ALLOW_EMPTY))
@@ -1013,6 +1060,16 @@
 	return 1;
 }
 
+void cleanup_message(struct strbuf *msgbuf,
+	enum commit_msg_cleanup_mode cleanup_mode, int verbose)
+{
+	if (verbose || /* Truncate the message just before the diff, if any. */
+	    cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+		strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
+	if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+		strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+}
+
 /*
  * Find out if the message in the strbuf contains only whitespace and
  * Signed-off-by lines.
@@ -1383,8 +1440,13 @@
 		msg = &commit_msg;
 	}
 
-	cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
-					  opts->default_msg_cleanup;
+	if (flags & CLEANUP_MSG)
+		cleanup = COMMIT_MSG_CLEANUP_ALL;
+	else if ((opts->signoff || opts->record_origin) &&
+		 !opts->explicit_cleanup)
+		cleanup = COMMIT_MSG_CLEANUP_SPACE;
+	else
+		cleanup = opts->default_msg_cleanup;
 
 	if (cleanup != COMMIT_MSG_CLEANUP_NONE)
 		strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
@@ -1513,32 +1575,6 @@
 		return 1;
 }
 
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter.  When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
-	/* commands that handle commits */
-	TODO_PICK = 0,
-	TODO_REVERT,
-	TODO_EDIT,
-	TODO_REWORD,
-	TODO_FIXUP,
-	TODO_SQUASH,
-	/* commands that do something else than handling a single commit */
-	TODO_EXEC,
-	TODO_BREAK,
-	TODO_LABEL,
-	TODO_RESET,
-	TODO_MERGE,
-	/* commands that do nothing but are counted for reporting progress */
-	TODO_NOOP,
-	TODO_DROP,
-	/* comments (not counted for reporting progress) */
-	TODO_COMMENT
-};
-
 static struct {
 	char c;
 	const char *str;
@@ -2021,26 +2057,7 @@
 	TODO_EDIT_MERGE_MSG = 1
 };
 
-struct todo_item {
-	enum todo_command command;
-	struct commit *commit;
-	unsigned int flags;
-	const char *arg;
-	int arg_len;
-	size_t offset_in_buf;
-};
-
-struct todo_list {
-	struct strbuf buf;
-	struct todo_item *items;
-	int nr, alloc, current;
-	int done_nr, total_nr;
-	struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
 {
 	strbuf_release(&todo_list->buf);
 	FREE_AND_NULL(todo_list->items);
@@ -2053,8 +2070,14 @@
 	return todo_list->items + todo_list->nr++;
 }
 
+const char *todo_item_get_arg(struct todo_list *todo_list,
+			      struct todo_item *item)
+{
+	return todo_list->buf.buf + item->arg_offset;
+}
+
 static int parse_insn_line(struct repository *r, struct todo_item *item,
-			   const char *bol, char *eol)
+			   const char *buf, const char *bol, char *eol)
 {
 	struct object_id commit_oid;
 	char *end_of_object_name;
@@ -2068,7 +2091,7 @@
 	if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
 		item->command = TODO_COMMENT;
 		item->commit = NULL;
-		item->arg = bol;
+		item->arg_offset = bol - buf;
 		item->arg_len = eol - bol;
 		return 0;
 	}
@@ -2095,7 +2118,7 @@
 			return error(_("%s does not accept arguments: '%s'"),
 				     command_to_string(item->command), bol);
 		item->commit = NULL;
-		item->arg = bol;
+		item->arg_offset = bol - buf;
 		item->arg_len = eol - bol;
 		return 0;
 	}
@@ -2107,7 +2130,7 @@
 	if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
 	    item->command == TODO_RESET) {
 		item->commit = NULL;
-		item->arg = bol;
+		item->arg_offset = bol - buf;
 		item->arg_len = (int)(eol - bol);
 		return 0;
 	}
@@ -2121,7 +2144,7 @@
 		} else {
 			item->flags |= TODO_EDIT_MERGE_MSG;
 			item->commit = NULL;
-			item->arg = bol;
+			item->arg_offset = bol - buf;
 			item->arg_len = (int)(eol - bol);
 			return 0;
 		}
@@ -2133,8 +2156,9 @@
 	status = get_oid(bol, &commit_oid);
 	*end_of_object_name = saved;
 
-	item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
-	item->arg_len = (int)(eol - item->arg);
+	bol = end_of_object_name + strspn(end_of_object_name, " \t");
+	item->arg_offset = bol - buf;
+	item->arg_len = (int)(eol - bol);
 
 	if (status < 0)
 		return error(_("could not parse '%.*s'"),
@@ -2144,13 +2168,15 @@
 	return !item->commit;
 }
 
-static int parse_insn_buffer(struct repository *r, char *buf,
-			     struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+				struct todo_list *todo_list)
 {
 	struct todo_item *item;
 	char *p = buf, *next_p;
 	int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
+	todo_list->current = todo_list->nr = 0;
+
 	for (i = 1; *p; i++, p = next_p) {
 		char *eol = strchrnul(p, '\n');
 
@@ -2161,10 +2187,13 @@
 
 		item = append_new_todo(todo_list);
 		item->offset_in_buf = p - todo_list->buf.buf;
-		if (parse_insn_line(r, item, p, eol)) {
+		if (parse_insn_line(r, item, buf, p, eol)) {
 			res = error(_("invalid line %d: %.*s"),
 				i, (int)(eol - p), p);
-			item->command = TODO_NOOP;
+			item->command = TODO_COMMENT + 1;
+			item->arg_offset = p - buf;
+			item->arg_len = (int)(eol - p);
+			item->commit = NULL;
 		}
 
 		if (fixup_okay)
@@ -2239,7 +2268,7 @@
 		return error(_("could not stat '%s'"), todo_file);
 	fill_stat_data(&todo_list->stat, &st);
 
-	res = parse_insn_buffer(r, todo_list->buf.buf, todo_list);
+	res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
 	if (res) {
 		if (is_rebase_i(opts))
 			return error(_("please fix this using "
@@ -2270,7 +2299,7 @@
 		FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
 		if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
-		    !parse_insn_buffer(r, done.buf.buf, &done))
+		    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
 			todo_list->done_nr = count_commands(&done);
 		else
 			todo_list->done_nr = 0;
@@ -2309,6 +2338,15 @@
 		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
 	else if (!strcmp(key, "options.edit"))
 		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.allow-empty"))
+		opts->allow_empty =
+			git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.allow-empty-message"))
+		opts->allow_empty_message =
+			git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "options.keep-redundant-commits"))
+		opts->keep_redundant_commits =
+			git_config_bool_or_int(key, value, &error_flag);
 	else if (!strcmp(key, "options.signoff"))
 		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
 	else if (!strcmp(key, "options.record-origin"))
@@ -2328,7 +2366,10 @@
 		opts->allow_rerere_auto =
 			git_config_bool_or_int(key, value, &error_flag) ?
 				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
-	else
+	else if (!strcmp(key, "options.default-msg-cleanup")) {
+		opts->explicit_cleanup = 1;
+		opts->default_msg_cleanup = get_cleanup_mode(value, 1);
+	} else
 		return error(_("invalid key: %s"), key);
 
 	if (!error_flag)
@@ -2507,7 +2548,7 @@
 
 		item->command = command;
 		item->commit = commit;
-		item->arg = NULL;
+		item->arg_offset = 0;
 		item->arg_len = 0;
 		item->offset_in_buf = todo_list->buf.len;
 		subject_len = find_commit_subject(commit_buffer, &subject);
@@ -2702,36 +2743,59 @@
 	int res = 0;
 
 	if (opts->no_commit)
-		res |= git_config_set_in_file_gently(opts_file, "options.no-commit", "true");
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.no-commit", "true");
 	if (opts->edit)
-		res |= git_config_set_in_file_gently(opts_file, "options.edit", "true");
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.edit", "true");
+	if (opts->allow_empty)
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.allow-empty", "true");
+	if (opts->allow_empty_message)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.allow-empty-message", "true");
+	if (opts->keep_redundant_commits)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.keep-redundant-commits", "true");
 	if (opts->signoff)
-		res |= git_config_set_in_file_gently(opts_file, "options.signoff", "true");
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.signoff", "true");
 	if (opts->record_origin)
-		res |= git_config_set_in_file_gently(opts_file, "options.record-origin", "true");
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.record-origin", "true");
 	if (opts->allow_ff)
-		res |= git_config_set_in_file_gently(opts_file, "options.allow-ff", "true");
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.allow-ff", "true");
 	if (opts->mainline) {
 		struct strbuf buf = STRBUF_INIT;
 		strbuf_addf(&buf, "%d", opts->mainline);
-		res |= git_config_set_in_file_gently(opts_file, "options.mainline", buf.buf);
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.mainline", buf.buf);
 		strbuf_release(&buf);
 	}
 	if (opts->strategy)
-		res |= git_config_set_in_file_gently(opts_file, "options.strategy", opts->strategy);
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.strategy", opts->strategy);
 	if (opts->gpg_sign)
-		res |= git_config_set_in_file_gently(opts_file, "options.gpg-sign", opts->gpg_sign);
+		res |= git_config_set_in_file_gently(opts_file,
+					"options.gpg-sign", opts->gpg_sign);
 	if (opts->xopts) {
 		int i;
 		for (i = 0; i < opts->xopts_nr; i++)
 			res |= git_config_set_multivar_in_file_gently(opts_file,
-							"options.strategy-option",
-							opts->xopts[i], "^$", 0);
+					"options.strategy-option",
+					opts->xopts[i], "^$", 0);
 	}
 	if (opts->allow_rerere_auto)
-		res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
-						     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
-						     "true" : "false");
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.allow-rerere-auto",
+				opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
+				"true" : "false");
+
+	if (opts->explicit_cleanup)
+		res |= git_config_set_in_file_gently(opts_file,
+				"options.default-msg-cleanup",
+				describe_cleanup_mode(opts->default_msg_cleanup));
 	return res;
 }
 
@@ -3546,6 +3610,8 @@
 
 	while (todo_list->current < todo_list->nr) {
 		struct todo_item *item = todo_list->items + todo_list->current;
+		const char *arg = todo_item_get_arg(todo_list, item);
+
 		if (save_todo(todo_list, opts))
 			return -1;
 		if (is_rebase_i(opts)) {
@@ -3598,10 +3664,9 @@
 					fprintf(stderr,
 						_("Stopped at %s...  %.*s\n"),
 						short_commit_name(commit),
-						item->arg_len, item->arg);
+						item->arg_len, arg);
 				return error_with_patch(r, commit,
-					item->arg, item->arg_len, opts, res,
-					!res);
+					arg, item->arg_len, opts, res, !res);
 			}
 			if (is_rebase_i(opts) && !res)
 				record_in_rewritten(&item->commit->object.oid,
@@ -3610,7 +3675,7 @@
 				if (res == 1)
 					intend_to_amend();
 				return error_failed_squash(r, item->commit, opts,
-					item->arg_len, item->arg);
+					item->arg_len, arg);
 			} else if (res && is_rebase_i(opts) && item->commit) {
 				int to_amend = 0;
 				struct object_id oid;
@@ -3629,16 +3694,16 @@
 					to_amend = 1;
 
 				return res | error_with_patch(r, item->commit,
-						item->arg, item->arg_len, opts,
+						arg, item->arg_len, opts,
 						res, to_amend);
 			}
 		} else if (item->command == TODO_EXEC) {
-			char *end_of_arg = (char *)(item->arg + item->arg_len);
+			char *end_of_arg = (char *)(arg + item->arg_len);
 			int saved = *end_of_arg;
 			struct stat st;
 
 			*end_of_arg = '\0';
-			res = do_exec(r, item->arg);
+			res = do_exec(r, arg);
 			*end_of_arg = saved;
 
 			if (res) {
@@ -3656,14 +3721,14 @@
 				todo_list->current = -1;
 			}
 		} else if (item->command == TODO_LABEL) {
-			if ((res = do_label(r, item->arg, item->arg_len)))
+			if ((res = do_label(r, arg, item->arg_len)))
 				reschedule = 1;
 		} else if (item->command == TODO_RESET) {
-			if ((res = do_reset(r, item->arg, item->arg_len, opts)))
+			if ((res = do_reset(r, arg, item->arg_len, opts)))
 				reschedule = 1;
 		} else if (item->command == TODO_MERGE) {
 			if ((res = do_merge(r, item->commit,
-					    item->arg, item->arg_len,
+					    arg, item->arg_len,
 					    item->flags, opts)) < 0)
 				reschedule = 1;
 			else if (item->commit)
@@ -3672,9 +3737,8 @@
 			if (res > 0)
 				/* failed with merge conflicts */
 				return error_with_patch(r, item->commit,
-							item->arg,
-							item->arg_len, opts,
-							res, 0);
+							arg, item->arg_len,
+							opts, res, 0);
 		} else if (!is_noop(item->command))
 			return error(_("unknown command %d"), item->command);
 
@@ -3689,9 +3753,8 @@
 			if (item->commit)
 				return error_with_patch(r,
 							item->commit,
-							item->arg,
-							item->arg_len, opts,
-							res, 0);
+							arg, item->arg_len,
+							opts, res, 0);
 		}
 
 		todo_list->current++;
@@ -4268,7 +4331,7 @@
 }
 
 static int make_script_with_merges(struct pretty_print_context *pp,
-				   struct rev_info *revs, FILE *out,
+				   struct rev_info *revs, struct strbuf *out,
 				   unsigned flags)
 {
 	int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4413,7 +4476,7 @@
 	 * gathering commits not yet shown, reversing the list on the fly,
 	 * then outputting that list (labeling revisions as needed).
 	 */
-	fprintf(out, "%s onto\n", cmd_label);
+	strbuf_addf(out, "%s onto\n", cmd_label);
 	for (iter = tips; iter; iter = iter->next) {
 		struct commit_list *list = NULL, *iter2;
 
@@ -4423,9 +4486,9 @@
 		entry = oidmap_get(&state.commit2label, &commit->object.oid);
 
 		if (entry)
-			fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+			strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
 		else
-			fprintf(out, "\n");
+			strbuf_addch(out, '\n');
 
 		while (oidset_contains(&interesting, &commit->object.oid) &&
 		       !oidset_contains(&shown, &commit->object.oid)) {
@@ -4438,8 +4501,8 @@
 		}
 
 		if (!commit)
-			fprintf(out, "%s %s\n", cmd_reset,
-				rebase_cousins ? "onto" : "[new root]");
+			strbuf_addf(out, "%s %s\n", cmd_reset,
+				    rebase_cousins ? "onto" : "[new root]");
 		else {
 			const char *to = NULL;
 
@@ -4452,12 +4515,12 @@
 					       &state);
 
 			if (!to || !strcmp(to, "onto"))
-				fprintf(out, "%s onto\n", cmd_reset);
+				strbuf_addf(out, "%s onto\n", cmd_reset);
 			else {
 				strbuf_reset(&oneline);
 				pretty_print_commit(pp, commit, &oneline);
-				fprintf(out, "%s %s # %s\n",
-					cmd_reset, to, oneline.buf);
+				strbuf_addf(out, "%s %s # %s\n",
+					    cmd_reset, to, oneline.buf);
 			}
 		}
 
@@ -4466,11 +4529,11 @@
 			entry = oidmap_get(&commit2todo, oid);
 			/* only show if not already upstream */
 			if (entry)
-				fprintf(out, "%s\n", entry->string);
+				strbuf_addf(out, "%s\n", entry->string);
 			entry = oidmap_get(&state.commit2label, oid);
 			if (entry)
-				fprintf(out, "%s %s\n",
-					cmd_label, entry->string);
+				strbuf_addf(out, "%s %s\n",
+					    cmd_label, entry->string);
 			oidset_insert(&shown, oid);
 		}
 
@@ -4492,13 +4555,11 @@
 	return 0;
 }
 
-int sequencer_make_script(struct repository *r, FILE *out,
-			  int argc, const char **argv,
-			  unsigned flags)
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+			  const char **argv, unsigned flags)
 {
 	char *format = NULL;
 	struct pretty_print_context pp = {0};
-	struct strbuf buf = STRBUF_INIT;
 	struct rev_info revs;
 	struct commit *commit;
 	int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4541,16 +4602,13 @@
 
 		if (!is_empty && (commit->object.flags & PATCHSAME))
 			continue;
-		strbuf_reset(&buf);
 		if (!keep_empty && is_empty)
-			strbuf_addf(&buf, "%c ", comment_line_char);
-		strbuf_addf(&buf, "%s %s ", insn,
+			strbuf_addf(out, "%c ", comment_line_char);
+		strbuf_addf(out, "%s %s ", insn,
 			    oid_to_hex(&commit->object.oid));
-		pretty_print_commit(&pp, commit, &buf);
-		strbuf_addch(&buf, '\n');
-		fputs(buf.buf, out);
+		pretty_print_commit(&pp, commit, out);
+		strbuf_addch(out, '\n');
 	}
-	strbuf_release(&buf);
 	return 0;
 }
 
@@ -4558,91 +4616,95 @@
  * Add commands after pick and (series of) squash/fixup commands
  * in the todo list.
  */
-int sequencer_add_exec_commands(struct repository *r,
-				const char *commands)
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+				 struct string_list *commands)
 {
-	const char *todo_file = rebase_path_todo();
-	struct todo_list todo_list = TODO_LIST_INIT;
-	struct strbuf *buf = &todo_list.buf;
-	size_t offset = 0, commands_len = strlen(commands);
-	int i, insert;
+	struct strbuf *buf = &todo_list->buf;
+	size_t base_offset = buf->len;
+	int i, insert, nr = 0, alloc = 0;
+	struct todo_item *items = NULL, *base_items = NULL;
 
-	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-		return error(_("could not read '%s'."), todo_file);
+	base_items = xcalloc(commands->nr, sizeof(struct todo_item));
+	for (i = 0; i < commands->nr; i++) {
+		size_t command_len = strlen(commands->items[i].string);
 
-	if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
-		todo_list_release(&todo_list);
-		return error(_("unusable todo list: '%s'"), todo_file);
+		strbuf_addstr(buf, commands->items[i].string);
+		strbuf_addch(buf, '\n');
+
+		base_items[i].command = TODO_EXEC;
+		base_items[i].offset_in_buf = base_offset;
+		base_items[i].arg_offset = base_offset + strlen("exec ");
+		base_items[i].arg_len = command_len - strlen("exec ");
+
+		base_offset += command_len + 1;
 	}
 
 	/*
 	 * Insert <commands> after every pick. Here, fixup/squash chains
 	 * are considered part of the pick, so we insert the commands *after*
 	 * those chains if there are any.
+	 *
+	 * As we insert the exec commands immediatly after rearranging
+	 * any fixups and before the user edits the list, a fixup chain
+	 * can never contain comments (any comments are empty picks that
+	 * have been commented out because the user did not specify
+	 * --keep-empty).  So, it is safe to insert an exec command
+	 * without looking at the command following a comment.
 	 */
-	insert = -1;
-	for (i = 0; i < todo_list.nr; i++) {
-		enum todo_command command = todo_list.items[i].command;
+	insert = 0;
+	for (i = 0; i < todo_list->nr; i++) {
+		enum todo_command command = todo_list->items[i].command;
+		if (insert && !is_fixup(command)) {
+			ALLOC_GROW(items, nr + commands->nr, alloc);
+			COPY_ARRAY(items + nr, base_items, commands->nr);
+			nr += commands->nr;
 
-		if (insert >= 0) {
-			/* skip fixup/squash chains */
-			if (command == TODO_COMMENT)
-				continue;
-			else if (is_fixup(command)) {
-				insert = i + 1;
-				continue;
-			}
-			strbuf_insert(buf,
-				      todo_list.items[insert].offset_in_buf +
-				      offset, commands, commands_len);
-			offset += commands_len;
-			insert = -1;
+			insert = 0;
 		}
 
+		ALLOC_GROW(items, nr + 1, alloc);
+		items[nr++] = todo_list->items[i];
+
 		if (command == TODO_PICK || command == TODO_MERGE)
-			insert = i + 1;
+			insert = 1;
 	}
 
 	/* insert or append final <commands> */
-	if (insert >= 0 && insert < todo_list.nr)
-		strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
-			      offset, commands, commands_len);
-	else if (insert >= 0 || !offset)
-		strbuf_add(buf, commands, commands_len);
-
-	i = write_message(buf->buf, buf->len, todo_file, 0);
-	todo_list_release(&todo_list);
-	return i;
-}
-
-int transform_todos(struct repository *r, unsigned flags)
-{
-	const char *todo_file = rebase_path_todo();
-	struct todo_list todo_list = TODO_LIST_INIT;
-	struct strbuf buf = STRBUF_INIT;
-	struct todo_item *item;
-	int i;
-
-	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-		return error(_("could not read '%s'."), todo_file);
-
-	if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
-		todo_list_release(&todo_list);
-		return error(_("unusable todo list: '%s'"), todo_file);
+	if (insert || nr == todo_list->nr) {
+		ALLOC_GROW(items, nr + commands->nr, alloc);
+		COPY_ARRAY(items + nr, base_items, commands->nr);
+		nr += commands->nr;
 	}
 
-	for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+	free(base_items);
+	FREE_AND_NULL(todo_list->items);
+	todo_list->items = items;
+	todo_list->nr = nr;
+	todo_list->alloc = alloc;
+}
+
+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+				struct strbuf *buf, int num, unsigned flags)
+{
+	struct todo_item *item;
+	int i, max = todo_list->nr;
+
+	if (num > 0 && num < max)
+		max = num;
+
+	for (item = todo_list->items, i = 0; i < max; i++, item++) {
 		/* if the item is not a command write it and continue */
 		if (item->command >= TODO_COMMENT) {
-			strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+			strbuf_addf(buf, "%.*s\n", item->arg_len,
+				    todo_item_get_arg(todo_list, item));
 			continue;
 		}
 
 		/* add command to the buffer */
 		if (flags & TODO_LIST_ABBREVIATE_CMDS)
-			strbuf_addch(&buf, command_to_char(item->command));
+			strbuf_addch(buf, command_to_char(item->command));
 		else
-			strbuf_addstr(&buf, command_to_string(item->command));
+			strbuf_addstr(buf, command_to_string(item->command));
 
 		/* add commit id */
 		if (item->commit) {
@@ -4652,181 +4714,92 @@
 
 			if (item->command == TODO_MERGE) {
 				if (item->flags & TODO_EDIT_MERGE_MSG)
-					strbuf_addstr(&buf, " -c");
+					strbuf_addstr(buf, " -c");
 				else
-					strbuf_addstr(&buf, " -C");
+					strbuf_addstr(buf, " -C");
 			}
 
-			strbuf_addf(&buf, " %s", oid);
+			strbuf_addf(buf, " %s", oid);
 		}
 
 		/* add all the rest */
 		if (!item->arg_len)
-			strbuf_addch(&buf, '\n');
+			strbuf_addch(buf, '\n');
 		else
-			strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
+			strbuf_addf(buf, " %.*s\n", item->arg_len,
+				    todo_item_get_arg(todo_list, item));
 	}
-
-	i = write_message(buf.buf, buf.len, todo_file, 0);
-	todo_list_release(&todo_list);
-	return i;
 }
 
-enum missing_commit_check_level get_missing_commit_check_level(void)
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+			    const char *file, const char *shortrevisions,
+			    const char *shortonto, int num, unsigned flags)
 {
-	const char *value;
+	int res;
+	struct strbuf buf = STRBUF_INIT;
 
-	if (git_config_get_value("rebase.missingcommitscheck", &value) ||
-			!strcasecmp("ignore", value))
-		return MISSING_COMMIT_CHECK_IGNORE;
-	if (!strcasecmp("warn", value))
-		return MISSING_COMMIT_CHECK_WARN;
-	if (!strcasecmp("error", value))
-		return MISSING_COMMIT_CHECK_ERROR;
-	warning(_("unrecognized setting %s for option "
-		  "rebase.missingCommitsCheck. Ignoring."), value);
-	return MISSING_COMMIT_CHECK_IGNORE;
-}
+	todo_list_to_strbuf(r, todo_list, &buf, num, flags);
+	if (flags & TODO_LIST_APPEND_TODO_HELP)
+		append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+				 shortrevisions, shortonto, &buf);
 
-define_commit_slab(commit_seen, unsigned char);
-/*
- * Check if the user dropped some commits by mistake
- * Behaviour determined by rebase.missingCommitsCheck.
- * Check if there is an unrecognized command or a
- * bad SHA-1 in a command.
- */
-int check_todo_list(struct repository *r)
-{
-	enum missing_commit_check_level check_level = get_missing_commit_check_level();
-	struct strbuf todo_file = STRBUF_INIT;
-	struct todo_list todo_list = TODO_LIST_INIT;
-	struct strbuf missing = STRBUF_INIT;
-	int advise_to_edit_todo = 0, res = 0, i;
-	struct commit_seen commit_seen;
-
-	init_commit_seen(&commit_seen);
-
-	strbuf_addstr(&todo_file, rebase_path_todo());
-	if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
-		res = -1;
-		goto leave_check;
-	}
-	advise_to_edit_todo = res =
-		parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
-
-	if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
-		goto leave_check;
-
-	/* Mark the commits in git-rebase-todo as seen */
-	for (i = 0; i < todo_list.nr; i++) {
-		struct commit *commit = todo_list.items[i].commit;
-		if (commit)
-			*commit_seen_at(&commit_seen, commit) = 1;
-	}
-
-	todo_list_release(&todo_list);
-	strbuf_addstr(&todo_file, ".backup");
-	if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
-		res = -1;
-		goto leave_check;
-	}
-	strbuf_release(&todo_file);
-	res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
-
-	/* Find commits in git-rebase-todo.backup yet unseen */
-	for (i = todo_list.nr - 1; i >= 0; i--) {
-		struct todo_item *item = todo_list.items + i;
-		struct commit *commit = item->commit;
-		if (commit && !*commit_seen_at(&commit_seen, commit)) {
-			strbuf_addf(&missing, " - %s %.*s\n",
-				    short_commit_name(commit),
-				    item->arg_len, item->arg);
-			*commit_seen_at(&commit_seen, commit) = 1;
-		}
-	}
-
-	/* Warn about missing commits */
-	if (!missing.len)
-		goto leave_check;
-
-	if (check_level == MISSING_COMMIT_CHECK_ERROR)
-		advise_to_edit_todo = res = 1;
-
-	fprintf(stderr,
-		_("Warning: some commits may have been dropped accidentally.\n"
-		"Dropped commits (newer to older):\n"));
-
-	/* Make the list user-friendly and display */
-	fputs(missing.buf, stderr);
-	strbuf_release(&missing);
-
-	fprintf(stderr, _("To avoid this message, use \"drop\" to "
-		"explicitly remove a commit.\n\n"
-		"Use 'git config rebase.missingCommitsCheck' to change "
-		"the level of warnings.\n"
-		"The possible behaviours are: ignore, warn, error.\n\n"));
-
-leave_check:
-	clear_commit_seen(&commit_seen);
-	strbuf_release(&todo_file);
-	todo_list_release(&todo_list);
-
-	if (advise_to_edit_todo)
-		fprintf(stderr,
-			_("You can fix this with 'git rebase --edit-todo' "
-			  "and then run 'git rebase --continue'.\n"
-			  "Or you can abort the rebase with 'git rebase"
-			  " --abort'.\n"));
+	res = write_message(buf.buf, buf.len, file, 0);
+	strbuf_release(&buf);
 
 	return res;
 }
 
-static int rewrite_file(const char *path, const char *buf, size_t len)
+static const char edit_todo_list_advice[] =
+N_("You can fix this with 'git rebase --edit-todo' "
+"and then run 'git rebase --continue'.\n"
+"Or you can abort the rebase with 'git rebase"
+" --abort'.\n");
+
+int check_todo_list_from_file(struct repository *r)
 {
-	int rc = 0;
-	int fd = open(path, O_WRONLY | O_TRUNC);
-	if (fd < 0)
-		return error_errno(_("could not open '%s' for writing"), path);
-	if (write_in_full(fd, buf, len) < 0)
-		rc = error_errno(_("could not write to '%s'"), path);
-	if (close(fd) && !rc)
-		rc = error_errno(_("could not close '%s'"), path);
-	return rc;
+	struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
+	int res = 0;
+
+	if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
+		res = -1;
+		goto out;
+	}
+
+	if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
+		res = -1;
+		goto out;
+	}
+
+	res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
+	if (!res)
+		res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
+	if (!res)
+		res = todo_list_check(&old_todo, &new_todo);
+	if (res)
+		fprintf(stderr, _(edit_todo_list_advice));
+out:
+	todo_list_release(&old_todo);
+	todo_list_release(&new_todo);
+
+	return res;
 }
 
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
+static int skip_unnecessary_picks(struct repository *r,
+				  struct todo_list *todo_list,
+				  struct object_id *base_oid)
 {
-	const char *todo_file = rebase_path_todo();
-	struct strbuf buf = STRBUF_INIT;
-	struct todo_list todo_list = TODO_LIST_INIT;
 	struct object_id *parent_oid;
-	int fd, i;
+	int i;
 
-	if (!read_oneliner(&buf, rebase_path_onto(), 0))
-		return error(_("could not read 'onto'"));
-	if (get_oid(buf.buf, output_oid)) {
-		strbuf_release(&buf);
-		return error(_("need a HEAD to fixup"));
-	}
-	strbuf_release(&buf);
-
-	if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
-		return -1;
-	if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
-		todo_list_release(&todo_list);
-		return -1;
-	}
-
-	for (i = 0; i < todo_list.nr; i++) {
-		struct todo_item *item = todo_list.items + i;
+	for (i = 0; i < todo_list->nr; i++) {
+		struct todo_item *item = todo_list->items + i;
 
 		if (item->command >= TODO_NOOP)
 			continue;
 		if (item->command != TODO_PICK)
 			break;
 		if (parse_commit(item->commit)) {
-			todo_list_release(&todo_list);
 			return error(_("could not parse commit '%s'"),
 				oid_to_hex(&item->commit->object.oid));
 		}
@@ -4835,135 +4808,101 @@
 		if (item->commit->parents->next)
 			break; /* merge commit */
 		parent_oid = &item->commit->parents->item->object.oid;
-		if (!oideq(parent_oid, output_oid))
+		if (!oideq(parent_oid, base_oid))
 			break;
-		oidcpy(output_oid, &item->commit->object.oid);
+		oidcpy(base_oid, &item->commit->object.oid);
 	}
 	if (i > 0) {
-		int offset = get_item_line_offset(&todo_list, i);
 		const char *done_path = rebase_path_done();
 
-		fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
-		if (fd < 0) {
-			error_errno(_("could not open '%s' for writing"),
-				    done_path);
-			todo_list_release(&todo_list);
-			return -1;
-		}
-		if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+		if (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {
 			error_errno(_("could not write to '%s'"), done_path);
-			todo_list_release(&todo_list);
-			close(fd);
-			return -1;
-		}
-		close(fd);
-
-		if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
-				 todo_list.buf.len - offset) < 0) {
-			todo_list_release(&todo_list);
 			return -1;
 		}
 
-		todo_list.current = i;
-		if (is_fixup(peek_command(&todo_list, 0)))
-			record_in_rewritten(output_oid, peek_command(&todo_list, 0));
+		MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
+		todo_list->nr -= i;
+		todo_list->current = 0;
+
+		if (is_fixup(peek_command(todo_list, 0)))
+			record_in_rewritten(base_oid, peek_command(todo_list, 0));
 	}
 
-	todo_list_release(&todo_list);
-
 	return 0;
 }
 
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
 		    const char *shortrevisions, const char *onto_name,
-		    const char *onto, const char *orig_head, const char *cmd,
-		    unsigned autosquash)
+		    const char *onto, const char *orig_head, struct string_list *commands,
+		    unsigned autosquash, struct todo_list *todo_list)
 {
 	const char *shortonto, *todo_file = rebase_path_todo();
-	struct todo_list todo_list = TODO_LIST_INIT;
-	struct strbuf *buf = &(todo_list.buf);
+	struct todo_list new_todo = TODO_LIST_INIT;
+	struct strbuf *buf = &todo_list->buf;
 	struct object_id oid;
-	struct stat st;
+	int res;
 
 	get_oid(onto, &oid);
 	shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
 
-	if (!lstat(todo_file, &st) && st.st_size == 0 &&
-	    write_message("noop\n", 5, todo_file, 0))
-		return -1;
-
-	if (autosquash && rearrange_squash(r))
-		return -1;
-
-	if (cmd && *cmd)
-		sequencer_add_exec_commands(r, cmd);
-
-	if (strbuf_read_file(buf, todo_file, 0) < 0)
-		return error_errno(_("could not read '%s'."), todo_file);
-
-	if (parse_insn_buffer(r, buf->buf, &todo_list)) {
-		todo_list_release(&todo_list);
-		return error(_("unusable todo list: '%s'"), todo_file);
-	}
-
-	if (count_commands(&todo_list) == 0) {
-		apply_autostash(opts);
-		sequencer_remove_state(opts);
-		todo_list_release(&todo_list);
-
-		return error(_("nothing to do"));
-	}
-
-	strbuf_addch(buf, '\n');
-	strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
-				      "Rebase %s onto %s (%d commands)",
-				      count_commands(&todo_list)),
-			      shortrevisions, shortonto, count_commands(&todo_list));
-	append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
-
-	if (write_message(buf->buf, buf->len, todo_file, 0)) {
-		todo_list_release(&todo_list);
-		return -1;
-	}
-
-	if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
-		return error(_("could not copy '%s' to '%s'."), todo_file,
-			     rebase_path_todo_backup());
-
-	if (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))
-		return error(_("could not transform the todo list"));
-
-	strbuf_reset(buf);
-
-	if (launch_sequence_editor(todo_file, buf, NULL)) {
-		apply_autostash(opts);
-		sequencer_remove_state(opts);
-		todo_list_release(&todo_list);
-
-		return -1;
-	}
-
-	strbuf_stripspace(buf, 1);
 	if (buf->len == 0) {
+		struct todo_item *item = append_new_todo(todo_list);
+		item->command = TODO_NOOP;
+		item->commit = NULL;
+		item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
+	}
+
+	if (autosquash && todo_list_rearrange_squash(todo_list))
+		return -1;
+
+	if (commands->nr)
+		todo_list_add_exec_commands(todo_list, commands);
+
+	if (count_commands(todo_list) == 0) {
 		apply_autostash(opts);
 		sequencer_remove_state(opts);
-		todo_list_release(&todo_list);
 
 		return error(_("nothing to do"));
 	}
 
-	todo_list_release(&todo_list);
+	res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+			     shortonto, flags);
+	if (res == -1)
+		return -1;
+	else if (res == -2) {
+		apply_autostash(opts);
+		sequencer_remove_state(opts);
 
-	if (check_todo_list(r)) {
+		return -1;
+	} else if (res == -3) {
+		apply_autostash(opts);
+		sequencer_remove_state(opts);
+		todo_list_release(&new_todo);
+
+		return error(_("nothing to do"));
+	}
+
+	if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
+	    todo_list_check(todo_list, &new_todo)) {
+		fprintf(stderr, _(edit_todo_list_advice));
 		checkout_onto(opts, onto_name, onto, orig_head);
+		todo_list_release(&new_todo);
+
 		return -1;
 	}
 
-	if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
-		return error(_("could not transform the todo list"));
-
-	if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
+	if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
+		todo_list_release(&new_todo);
 		return error(_("could not skip unnecessary pick commands"));
+	}
+
+	if (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,
+				    flags & ~(TODO_LIST_SHORTEN_IDS))) {
+		todo_list_release(&new_todo);
+		return error_errno(_("could not write '%s'"), todo_file);
+	}
+
+	todo_list_release(&new_todo);
 
 	if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
 		return -1;
@@ -4998,21 +4937,13 @@
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(struct repository *r)
+int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
-	const char *todo_file = rebase_path_todo();
-	struct todo_list todo_list = TODO_LIST_INIT;
 	struct hashmap subject2item;
-	int res = 0, rearranged = 0, *next, *tail, i;
+	int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
 	char **subjects;
 	struct commit_todo_item commit_todo;
-
-	if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
-		return -1;
-	if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
-		todo_list_release(&todo_list);
-		return -1;
-	}
+	struct todo_item *items = NULL;
 
 	init_commit_todo_item(&commit_todo);
 	/*
@@ -5025,13 +4956,13 @@
 	 * be moved to appear after the i'th.
 	 */
 	hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
-		     NULL, todo_list.nr);
-	ALLOC_ARRAY(next, todo_list.nr);
-	ALLOC_ARRAY(tail, todo_list.nr);
-	ALLOC_ARRAY(subjects, todo_list.nr);
-	for (i = 0; i < todo_list.nr; i++) {
+		     NULL, todo_list->nr);
+	ALLOC_ARRAY(next, todo_list->nr);
+	ALLOC_ARRAY(tail, todo_list->nr);
+	ALLOC_ARRAY(subjects, todo_list->nr);
+	for (i = 0; i < todo_list->nr; i++) {
 		struct strbuf buf = STRBUF_INIT;
-		struct todo_item *item = todo_list.items + i;
+		struct todo_item *item = todo_list->items + i;
 		const char *commit_buffer, *subject, *p;
 		size_t subject_len;
 		int i2 = -1;
@@ -5044,7 +4975,6 @@
 		}
 
 		if (is_fixup(item->command)) {
-			todo_list_release(&todo_list);
 			clear_commit_todo_item(&commit_todo);
 			return error(_("the script was already rearranged."));
 		}
@@ -5079,7 +5009,7 @@
 				 *commit_todo_item_at(&commit_todo, commit2))
 				/* found by commit name */
 				i2 = *commit_todo_item_at(&commit_todo, commit2)
-					- todo_list.items;
+					- todo_list->items;
 			else {
 				/* copy can be a prefix of the commit subject */
 				for (i2 = 0; i2 < i; i2++)
@@ -5092,7 +5022,7 @@
 		}
 		if (i2 >= 0) {
 			rearranged = 1;
-			todo_list.items[i].command =
+			todo_list->items[i].command =
 				starts_with(subject, "fixup!") ?
 				TODO_FIXUP : TODO_SQUASH;
 			if (next[i2] < 0)
@@ -5110,10 +5040,8 @@
 	}
 
 	if (rearranged) {
-		struct strbuf buf = STRBUF_INIT;
-
-		for (i = 0; i < todo_list.nr; i++) {
-			enum todo_command command = todo_list.items[i].command;
+		for (i = 0; i < todo_list->nr; i++) {
+			enum todo_command command = todo_list->items[i].command;
 			int cur = i;
 
 			/*
@@ -5124,37 +5052,26 @@
 				continue;
 
 			while (cur >= 0) {
-				const char *bol =
-					get_item_line(&todo_list, cur);
-				const char *eol =
-					get_item_line(&todo_list, cur + 1);
-
-				/* replace 'pick', by 'fixup' or 'squash' */
-				command = todo_list.items[cur].command;
-				if (is_fixup(command)) {
-					strbuf_addstr(&buf,
-						todo_command_info[command].str);
-					bol += strcspn(bol, " \t");
-				}
-
-				strbuf_add(&buf, bol, eol - bol);
-
+				ALLOC_GROW(items, nr + 1, alloc);
+				items[nr++] = todo_list->items[cur];
 				cur = next[cur];
 			}
 		}
 
-		res = rewrite_file(todo_file, buf.buf, buf.len);
-		strbuf_release(&buf);
+		FREE_AND_NULL(todo_list->items);
+		todo_list->items = items;
+		todo_list->nr = nr;
+		todo_list->alloc = alloc;
 	}
 
 	free(next);
 	free(tail);
-	for (i = 0; i < todo_list.nr; i++)
+	for (i = 0; i < todo_list->nr; i++)
 		free(subjects[i]);
 	free(subjects);
 	hashmap_free(&subject2item, 1);
-	todo_list_release(&todo_list);
 
 	clear_commit_todo_item(&commit_todo);
-	return res;
+
+	return 0;
 }
diff --git a/sequencer.h b/sequencer.h
index 4d505b3..75e292c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -10,6 +10,7 @@
 const char *git_path_commit_editmsg(void);
 const char *git_path_seq_dir(void);
 const char *rebase_path_todo(void);
+const char *rebase_path_todo_backup(void);
 
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
@@ -47,6 +48,7 @@
 
 	char *gpg_sign;
 	enum commit_msg_cleanup_mode default_msg_cleanup;
+	int explicit_cleanup;
 
 	/* Merge strategy */
 	char *strategy;
@@ -66,14 +68,60 @@
 };
 #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
 
-enum missing_commit_check_level {
-	MISSING_COMMIT_CHECK_IGNORE = 0,
-	MISSING_COMMIT_CHECK_WARN,
-	MISSING_COMMIT_CHECK_ERROR
+/*
+ * Note that ordering matters in this enum. Not only must it match the mapping
+ * of todo_command_info (in sequencer.c), it is also divided into several
+ * sections that matter.  When adding new commands, make sure you add it in the
+ * right section.
+ */
+enum todo_command {
+	/* commands that handle commits */
+	TODO_PICK = 0,
+	TODO_REVERT,
+	TODO_EDIT,
+	TODO_REWORD,
+	TODO_FIXUP,
+	TODO_SQUASH,
+	/* commands that do something else than handling a single commit */
+	TODO_EXEC,
+	TODO_BREAK,
+	TODO_LABEL,
+	TODO_RESET,
+	TODO_MERGE,
+	/* commands that do nothing but are counted for reporting progress */
+	TODO_NOOP,
+	TODO_DROP,
+	/* comments (not counted for reporting progress) */
+	TODO_COMMENT
 };
 
-int write_message(const void *buf, size_t len, const char *filename,
-		  int append_eol);
+struct todo_item {
+	enum todo_command command;
+	struct commit *commit;
+	unsigned int flags;
+	int arg_len;
+	/* The offset of the command and its argument in the strbuf */
+	size_t offset_in_buf, arg_offset;
+};
+
+struct todo_list {
+	struct strbuf buf;
+	struct todo_item *items;
+	int nr, alloc, current;
+	int done_nr, total_nr;
+	struct stat_data stat;
+};
+
+#define TODO_LIST_INIT { STRBUF_INIT }
+
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+				struct todo_list *todo_list);
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+			    const char *file, const char *shortrevisions,
+			    const char *shortonto, int num, unsigned flags);
+void todo_list_release(struct todo_list *todo_list);
+const char *todo_item_get_arg(struct todo_list *todo_list,
+			      struct todo_item *item);
 
 /* Call this to setup defaults before parsing command line options */
 void sequencer_init_config(struct replay_opts *opts);
@@ -93,19 +141,19 @@
  * commits should be rebased onto the new base, this flag needs to be passed.
  */
 #define TODO_LIST_REBASE_COUSINS (1U << 4)
-int sequencer_make_script(struct repository *repo, FILE *out,
-			  int argc, const char **argv,
-			  unsigned flags);
+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
 
-int sequencer_add_exec_commands(struct repository *r, const char *command);
-int transform_todos(struct repository *r, unsigned flags);
-enum missing_commit_check_level get_missing_commit_check_level(void);
-int check_todo_list(struct repository *r);
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+			  const char **argv, unsigned flags);
+
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+				 struct string_list *commands);
+int check_todo_list_from_file(struct repository *r);
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
 		    const char *shortrevisions, const char *onto_name,
-		    const char *onto, const char *orig_head, const char *cmd,
-		    unsigned autosquash);
-int rearrange_squash(struct repository *r);
+		    const char *onto, const char *orig_head, struct string_list *commands,
+		    unsigned autosquash, struct todo_list *todo_list);
+int todo_list_rearrange_squash(struct todo_list *todo_list);
 
 /*
  * Append a signoff to the commit message in "msgbuf". The ignore_footer
@@ -115,7 +163,14 @@
  */
 void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 
-void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);
+void append_conflicts_hint(struct index_state *istate,
+		struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode);
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+	int use_editor);
+
+void cleanup_message(struct strbuf *msgbuf,
+	enum commit_msg_cleanup_mode cleanup_mode, int verbose);
+
 int message_is_empty(const struct strbuf *sb,
 		     enum commit_msg_cleanup_mode cleanup_mode);
 int template_untouched(const struct strbuf *sb, const char *template_file,
diff --git a/serve.h b/serve.h
index fe65ba9..42ddca7 100644
--- a/serve.h
+++ b/serve.h
@@ -2,14 +2,14 @@
 #define SERVE_H
 
 struct argv_array;
-extern int has_capability(const struct argv_array *keys, const char *capability,
-			  const char **value);
+int has_capability(const struct argv_array *keys, const char *capability,
+		   const char **value);
 
 struct serve_options {
 	unsigned advertise_capabilities;
 	unsigned stateless_rpc;
 };
 #define SERVE_OPTIONS_INIT { 0 }
-extern void serve(struct serve_options *options);
+void serve(struct serve_options *options);
 
 #endif /* SERVE_H */
diff --git a/server-info.c b/server-info.c
index e2b2d6a2..41274d0 100644
--- a/server-info.c
+++ b/server-info.c
@@ -78,7 +78,7 @@
 	return for_each_ref(add_info_ref, fp);
 }
 
-static int update_info_refs(int force)
+static int update_info_refs(void)
 {
 	char *path = git_pathdup("info/refs");
 	int ret = update_info_file(path, generate_info_refs);
@@ -91,19 +91,15 @@
 	struct packed_git *p;
 	int old_num;
 	int new_num;
-	int nr_alloc;
 } **info;
 static int num_pack;
-static const char *objdir;
-static int objdirlen;
 
 static struct pack_info *find_pack_by_name(const char *name)
 {
 	int i;
 	for (i = 0; i < num_pack; i++) {
 		struct packed_git *p = info[i]->p;
-		/* skip "/pack/" after ".git/objects" */
-		if (!strcmp(p->pack_name + objdirlen + 6, name))
+		if (!strcmp(pack_basename(p), name))
 			return info[i];
 	}
 	return NULL;
@@ -112,9 +108,9 @@
 /* Returns non-zero when we detect that the info in the
  * old file is useless.
  */
-static int parse_pack_def(const char *line, int old_cnt)
+static int parse_pack_def(const char *packname, int old_cnt)
 {
-	struct pack_info *i = find_pack_by_name(line + 2);
+	struct pack_info *i = find_pack_by_name(packname);
 	if (i) {
 		i->old_num = old_cnt;
 		return 0;
@@ -131,39 +127,40 @@
 static int read_pack_info_file(const char *infofile)
 {
 	FILE *fp;
-	char line[1000];
+	struct strbuf line = STRBUF_INIT;
 	int old_cnt = 0;
+	int stale = 1;
 
 	fp = fopen_or_warn(infofile, "r");
 	if (!fp)
 		return 1; /* nonexistent is not an error. */
 
-	while (fgets(line, sizeof(line), fp)) {
-		int len = strlen(line);
-		if (len && line[len-1] == '\n')
-			line[--len] = 0;
+	while (strbuf_getline(&line, fp) != EOF) {
+		const char *arg;
 
-		if (!len)
+		if (!line.len)
 			continue;
 
-		switch (line[0]) {
-		case 'P': /* P name */
-			if (parse_pack_def(line, old_cnt++))
+		if (skip_prefix(line.buf, "P ", &arg)) {
+			/* P name */
+			if (parse_pack_def(arg, old_cnt++))
 				goto out_stale;
-			break;
-		case 'D': /* we used to emit D but that was misguided. */
-		case 'T': /* we used to emit T but nobody uses it. */
+		} else if (line.buf[0] == 'D') {
+			/* we used to emit D but that was misguided. */
 			goto out_stale;
-		default:
-			error("unrecognized: %s", line);
-			break;
+		} else if (line.buf[0] == 'T') {
+			/* we used to emit T but nobody uses it. */
+			goto out_stale;
+		} else {
+			error("unrecognized: %s", line.buf);
 		}
 	}
-	fclose(fp);
-	return 0;
+	stale = 0;
+
  out_stale:
+	strbuf_release(&line);
 	fclose(fp);
-	return 1;
+	return stale;
 }
 
 static int compare_info(const void *a_, const void *b_)
@@ -196,9 +193,6 @@
 	int stale;
 	int i = 0;
 
-	objdir = get_object_directory();
-	objdirlen = strlen(objdir);
-
 	for (p = get_all_packs(the_repository); p; p = p->next) {
 		/* we ignore things on alternate path since they are
 		 * not available to the pullers in general.
@@ -212,6 +206,7 @@
 	for (i = 0, p = get_all_packs(the_repository); p; p = p->next) {
 		if (!p->pack_local)
 			continue;
+		assert(i < num_pack);
 		info[i] = xcalloc(1, sizeof(struct pack_info));
 		info[i]->p = p;
 		info[i]->old_num = -1;
@@ -245,7 +240,7 @@
 {
 	int i;
 	for (i = 0; i < num_pack; i++) {
-		if (fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6) < 0)
+		if (fprintf(fp, "P %s\n", pack_basename(info[i]->p)) < 0)
 			return -1;
 	}
 	if (fputc('\n', fp) == EOF)
@@ -274,7 +269,7 @@
 	 */
 	int errs = 0;
 
-	errs = errs | update_info_refs(force);
+	errs = errs | update_info_refs();
 	errs = errs | update_info_packs(force);
 
 	/* remove leftover rev-cache file if there is any */
diff --git a/setup.c b/setup.c
index d0c958c..8dcb463 100644
--- a/setup.c
+++ b/setup.c
@@ -164,7 +164,8 @@
 	die_errno(_("failed to stat '%s'"), arg);
 }
 
-static void NORETURN die_verify_filename(const char *prefix,
+static void NORETURN die_verify_filename(struct repository *r,
+					 const char *prefix,
 					 const char *arg,
 					 int diagnose_misspelt_rev)
 {
@@ -179,7 +180,7 @@
 	 * let maybe_die_on_misspelt_object_name() even trigger.
 	 */
 	if (!(arg[0] == ':' && !isalnum(arg[1])))
-		maybe_die_on_misspelt_object_name(arg, prefix);
+		maybe_die_on_misspelt_object_name(r, arg, prefix);
 
 	/* ... or fall back the most general message. */
 	die(_("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
@@ -234,7 +235,7 @@
 		die(_("option '%s' must come before non-option arguments"), arg);
 	if (looks_like_pathspec(arg) || check_filename(prefix, arg))
 		return;
-	die_verify_filename(prefix, arg, diagnose_misspelt_rev);
+	die_verify_filename(the_repository, prefix, arg, diagnose_misspelt_rev);
 }
 
 /*
diff --git a/sha1-file.c b/sha1-file.c
index 494606f..ed5c50d 100644
--- a/sha1-file.c
+++ b/sha1-file.c
@@ -189,6 +189,14 @@
 	return GIT_HASH_UNKNOWN;
 }
 
+int hash_algo_by_length(int len)
+{
+	int i;
+	for (i = 1; i < GIT_HASH_NALGOS; i++)
+		if (len == hash_algos[i].rawsz)
+			return i;
+	return GIT_HASH_UNKNOWN;
+}
 
 /*
  * This is meant to hold a *small* number of objects that you would
@@ -1370,7 +1378,8 @@
 
 		/* Check if it is a missing object */
 		if (fetch_if_missing && repository_format_partial_clone &&
-		    !already_retried && r == the_repository) {
+		    !already_retried && r == the_repository &&
+		    !(flags & OBJECT_INFO_FOR_PREFETCH)) {
 			/*
 			 * TODO Investigate having fetch_object() return
 			 * TODO error/success and stopping the music here.
diff --git a/sha1-lookup.h b/sha1-lookup.h
index 7678b23..5afcd01 100644
--- a/sha1-lookup.h
+++ b/sha1-lookup.h
@@ -3,10 +3,10 @@
 
 typedef const unsigned char *sha1_access_fn(size_t index, void *table);
 
-extern int sha1_pos(const unsigned char *sha1,
-		    void *table,
-		    size_t nr,
-		    sha1_access_fn fn);
+int sha1_pos(const unsigned char *sha1,
+	     void *table,
+	     size_t nr,
+	     sha1_access_fn fn);
 
 /*
  * Searches for sha1 in table, using the given fanout table to determine the
diff --git a/sha1-name.c b/sha1-name.c
index 07c71a7..775a73d 100644
--- a/sha1-name.c
+++ b/sha1-name.c
@@ -12,18 +12,20 @@
 #include "packfile.h"
 #include "object-store.h"
 #include "repository.h"
+#include "submodule.h"
 #include "midx.h"
 #include "commit-reach.h"
 
-static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
+static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
 
-typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
+typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *);
 
 struct disambiguate_state {
 	int len; /* length of prefix in hex chars */
 	char hex_pfx[GIT_MAX_HEXSZ + 1];
 	struct object_id bin_pfx;
 
+	struct repository *repo;
 	disambiguate_hint_fn fn;
 	void *cb_data;
 	struct object_id candidate;
@@ -38,7 +40,7 @@
 static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
 	if (ds->always_call_fn) {
-		ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+		ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0;
 		return;
 	}
 	if (!ds->candidate_exists) {
@@ -58,7 +60,7 @@
 	}
 
 	if (!ds->candidate_checked) {
-		ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
+		ds->candidate_ok = ds->fn(ds->repo, &ds->candidate, ds->cb_data);
 		ds->disambiguate_fn_used = 1;
 		ds->candidate_checked = 1;
 	}
@@ -71,7 +73,7 @@
 	}
 
 	/* if we reach this point, we know ds->candidate satisfies fn */
-	if (ds->fn(current, ds->cb_data)) {
+	if (ds->fn(ds->repo, current, ds->cb_data)) {
 		/*
 		 * if both current and candidate satisfy fn, we cannot
 		 * disambiguate.
@@ -89,9 +91,7 @@
 {
 	struct object_directory *odb;
 
-	for (odb = the_repository->objects->odb;
-	     odb && !ds->ambiguous;
-	     odb = odb->next) {
+	for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) {
 		int pos;
 		struct oid_array *loose_objects;
 
@@ -182,10 +182,10 @@
 	struct multi_pack_index *m;
 	struct packed_git *p;
 
-	for (m = get_multi_pack_index(the_repository); m && !ds->ambiguous;
+	for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous;
 	     m = m->next)
 		unique_in_midx(m, ds);
-	for (p = get_packed_git(the_repository); p && !ds->ambiguous;
+	for (p = get_packed_git(ds->repo); p && !ds->ambiguous;
 	     p = p->next)
 		unique_in_pack(p, ds);
 }
@@ -215,7 +215,7 @@
 		 * same repository!
 		 */
 		ds->candidate_ok = (!ds->disambiguate_fn_used ||
-				    ds->fn(&ds->candidate, ds->cb_data));
+				    ds->fn(ds->repo, &ds->candidate, ds->cb_data));
 
 	if (!ds->candidate_ok)
 		return SHORT_NAME_AMBIGUOUS;
@@ -224,59 +224,67 @@
 	return 0;
 }
 
-static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_commit_only(struct repository *r,
+				    const struct object_id *oid,
+				    void *cb_data_unused)
 {
-	int kind = oid_object_info(the_repository, oid, NULL);
+	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_COMMIT;
 }
 
-static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_committish_only(struct repository *r,
+					const struct object_id *oid,
+					void *cb_data_unused)
 {
 	struct object *obj;
 	int kind;
 
-	kind = oid_object_info(the_repository, oid, NULL);
+	kind = oid_object_info(r, oid, NULL);
 	if (kind == OBJ_COMMIT)
 		return 1;
 	if (kind != OBJ_TAG)
 		return 0;
 
 	/* We need to do this the hard way... */
-	obj = deref_tag(the_repository, parse_object(the_repository, oid),
-			NULL, 0);
+	obj = deref_tag(r, parse_object(r, oid), NULL, 0);
 	if (obj && obj->type == OBJ_COMMIT)
 		return 1;
 	return 0;
 }
 
-static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_tree_only(struct repository *r,
+				  const struct object_id *oid,
+				  void *cb_data_unused)
 {
-	int kind = oid_object_info(the_repository, oid, NULL);
+	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_TREE;
 }
 
-static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_treeish_only(struct repository *r,
+				     const struct object_id *oid,
+				     void *cb_data_unused)
 {
 	struct object *obj;
 	int kind;
 
-	kind = oid_object_info(the_repository, oid, NULL);
+	kind = oid_object_info(r, oid, NULL);
 	if (kind == OBJ_TREE || kind == OBJ_COMMIT)
 		return 1;
 	if (kind != OBJ_TAG)
 		return 0;
 
 	/* We need to do this the hard way... */
-	obj = deref_tag(the_repository, parse_object(the_repository, oid),
-			NULL, 0);
+	obj = deref_tag(r, parse_object(r, oid), NULL, 0);
 	if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
 		return 1;
 	return 0;
 }
 
-static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
+static int disambiguate_blob_only(struct repository *r,
+				  const struct object_id *oid,
+				  void *cb_data_unused)
 {
-	int kind = oid_object_info(the_repository, oid, NULL);
+	int kind = oid_object_info(r, oid, NULL);
 	return kind == OBJ_BLOB;
 }
 
@@ -310,7 +318,8 @@
 	return error("unknown hint type for '%s': %s", var, value);
 }
 
-static int init_object_disambiguation(const char *name, int len,
+static int init_object_disambiguation(struct repository *r,
+				      const char *name, int len,
 				      struct disambiguate_state *ds)
 {
 	int i;
@@ -341,7 +350,8 @@
 
 	ds->len = len;
 	ds->hex_pfx[len] = '\0';
-	prepare_alt_odb(the_repository);
+	ds->repo = r;
+	prepare_alt_odb(r);
 	return 0;
 }
 
@@ -351,25 +361,25 @@
 	struct strbuf desc = STRBUF_INIT;
 	int type;
 
-	if (ds->fn && !ds->fn(oid, ds->cb_data))
+	if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
 		return 0;
 
-	type = oid_object_info(the_repository, oid, NULL);
+	type = oid_object_info(ds->repo, oid, NULL);
 	if (type == OBJ_COMMIT) {
-		struct commit *commit = lookup_commit(the_repository, oid);
+		struct commit *commit = lookup_commit(ds->repo, oid);
 		if (commit) {
 			struct pretty_print_context pp = {0};
 			pp.date_mode.type = DATE_SHORT;
 			format_commit_message(commit, " %ad - %s", &desc, &pp);
 		}
 	} else if (type == OBJ_TAG) {
-		struct tag *tag = lookup_tag(the_repository, oid);
+		struct tag *tag = lookup_tag(ds->repo, oid);
 		if (!parse_tag(tag) && tag->tag)
 			strbuf_addf(&desc, " %s", tag->tag);
 	}
 
 	advise("  %s %s%s",
-	       find_unique_abbrev(oid, DEFAULT_ABBREV),
+	       repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),
 	       type_name(type) ? type_name(type) : "unknown type",
 	       desc.buf);
 
@@ -383,10 +393,18 @@
 	return 0;
 }
 
+static int repo_collect_ambiguous(struct repository *r,
+				  const struct object_id *oid,
+				  void *data)
+{
+	return collect_ambiguous(oid, data);
+}
+
+static struct repository *sort_ambiguous_repo;
 static int sort_ambiguous(const void *a, const void *b)
 {
-	int a_type = oid_object_info(the_repository, a, NULL);
-	int b_type = oid_object_info(the_repository, b, NULL);
+	int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
+	int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
 	int a_type_sort;
 	int b_type_sort;
 
@@ -411,7 +429,16 @@
 	return a_type_sort > b_type_sort ? 1 : -1;
 }
 
-static enum get_oid_result get_short_oid(const char *name, int len,
+static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a)
+{
+	/* mutex will be needed if this code is to be made thread safe */
+	sort_ambiguous_repo = r;
+	QSORT(a->oid, a->nr, sort_ambiguous);
+	sort_ambiguous_repo = NULL;
+}
+
+static enum get_oid_result get_short_oid(struct repository *r,
+					 const char *name, int len,
 					 struct object_id *oid,
 					 unsigned flags)
 {
@@ -419,7 +446,7 @@
 	struct disambiguate_state ds;
 	int quietly = !!(flags & GET_OID_QUIETLY);
 
-	if (init_object_disambiguation(name, len, &ds) < 0)
+	if (init_object_disambiguation(r, name, len, &ds) < 0)
 		return -1;
 
 	if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
@@ -469,8 +496,8 @@
 			ds.fn = NULL;
 
 		advise(_("The candidates are:"));
-		for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect);
-		QSORT(collect.oid, collect.nr, sort_ambiguous);
+		repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
+		sort_ambiguous_oid_array(r, &collect);
 
 		if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
 			BUG("show_ambiguous_object shouldn't return non-zero");
@@ -480,17 +507,18 @@
 	return status;
 }
 
-int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+int repo_for_each_abbrev(struct repository *r, const char *prefix,
+			 each_abbrev_fn fn, void *cb_data)
 {
 	struct oid_array collect = OID_ARRAY_INIT;
 	struct disambiguate_state ds;
 	int ret;
 
-	if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
+	if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0)
 		return -1;
 
 	ds.always_call_fn = 1;
-	ds.fn = collect_ambiguous;
+	ds.fn = repo_collect_ambiguous;
 	ds.cb_data = &collect;
 	find_short_object_filename(&ds);
 	find_short_packed_object(&ds);
@@ -517,6 +545,7 @@
 	unsigned int init_len;
 	unsigned int cur_len;
 	char *hex;
+	struct repository *repo;
 	const struct object_id *oid;
 };
 
@@ -545,6 +574,13 @@
 	return 0;
 }
 
+static int repo_extend_abbrev_len(struct repository *r,
+				  const struct object_id *oid,
+				  void *cb_data)
+{
+	return extend_abbrev_len(oid, cb_data);
+}
+
 static void find_abbrev_len_for_midx(struct multi_pack_index *m,
 				     struct min_abbrev_data *mad)
 {
@@ -622,21 +658,22 @@
 	struct multi_pack_index *m;
 	struct packed_git *p;
 
-	for (m = get_multi_pack_index(the_repository); m; m = m->next)
+	for (m = get_multi_pack_index(mad->repo); m; m = m->next)
 		find_abbrev_len_for_midx(m, mad);
-	for (p = get_packed_git(the_repository); p; p = p->next)
+	for (p = get_packed_git(mad->repo); p; p = p->next)
 		find_abbrev_len_for_pack(p, mad);
 }
 
-int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
+int repo_find_unique_abbrev_r(struct repository *r, char *hex,
+			      const struct object_id *oid, int len)
 {
 	struct disambiguate_state ds;
 	struct min_abbrev_data mad;
 	struct object_id oid_ret;
-	const unsigned hexsz = the_hash_algo->hexsz;
+	const unsigned hexsz = r->hash_algo->hexsz;
 
 	if (len < 0) {
-		unsigned long count = approximate_object_count();
+		unsigned long count = repo_approximate_object_count(r);
 		/*
 		 * Add one because the MSB only tells us the highest bit set,
 		 * not including the value of all the _other_ bits (so "15"
@@ -661,6 +698,7 @@
 	if (len == hexsz || !len)
 		return hexsz;
 
+	mad.repo = r;
 	mad.init_len = len;
 	mad.cur_len = len;
 	mad.hex = hex;
@@ -668,10 +706,10 @@
 
 	find_abbrev_len_packed(&mad);
 
-	if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
+	if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0)
 		return -1;
 
-	ds.fn = extend_abbrev_len;
+	ds.fn = repo_extend_abbrev_len;
 	ds.always_call_fn = 1;
 	ds.cb_data = (void *)&mad;
 
@@ -682,13 +720,15 @@
 	return mad.cur_len;
 }
 
-const char *find_unique_abbrev(const struct object_id *oid, int len)
+const char *repo_find_unique_abbrev(struct repository *r,
+				    const struct object_id *oid,
+				    int len)
 {
 	static int bufno;
 	static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
 	char *hex = hexbuffer[bufno];
 	bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
-	find_unique_abbrev_r(hex, oid, len);
+	repo_find_unique_abbrev_r(r, hex, oid, len);
 	return hex;
 }
 
@@ -743,11 +783,11 @@
 	return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
 }
 
-static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
-static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
+static enum get_oid_result get_oid_1(struct repository *r, const char *name, int len, struct object_id *oid, unsigned lookup_flags);
+static int interpret_nth_prior_checkout(struct repository *r, const char *name, int namelen, struct strbuf *buf);
 
-static int get_oid_basic(const char *str, int len, struct object_id *oid,
-			  unsigned int flags)
+static int get_oid_basic(struct repository *r, const char *str, int len,
+			 struct object_id *oid, unsigned int flags)
 {
 	static const char *warn_msg = "refname '%.*s' is ambiguous.";
 	static const char *object_name_msg = N_(
@@ -765,9 +805,9 @@
 	int refs_found = 0;
 	int at, reflog_len, nth_prior = 0;
 
-	if (len == the_hash_algo->hexsz && !get_oid_hex(str, oid)) {
+	if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
 		if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
-			refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
+			refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref);
 			if (refs_found > 0) {
 				warning(warn_msg, len, str);
 				if (advice_object_name_warning)
@@ -808,8 +848,8 @@
 		struct strbuf buf = STRBUF_INIT;
 		int detached;
 
-		if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
-			detached = (buf.len == the_hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
+		if (interpret_nth_prior_checkout(r, str, len, &buf) > 0) {
+			detached = (buf.len == r->hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
 			strbuf_release(&buf);
 			if (detached)
 				return 0;
@@ -818,18 +858,18 @@
 
 	if (!len && reflog_len)
 		/* allow "@{...}" to mean the current branch reflog */
-		refs_found = dwim_ref("HEAD", 4, oid, &real_ref);
+		refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref);
 	else if (reflog_len)
-		refs_found = dwim_log(str, len, oid, &real_ref);
+		refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
 	else
-		refs_found = dwim_ref(str, len, oid, &real_ref);
+		refs_found = repo_dwim_ref(r, str, len, oid, &real_ref);
 
 	if (!refs_found)
 		return -1;
 
 	if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) &&
 	    (refs_found > 1 ||
-	     !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY)))
+	     !get_short_oid(r, str, len, &tmp_oid, GET_OID_QUIETLY)))
 		warning(warn_msg, len, str);
 
 	if (reflog_len) {
@@ -861,7 +901,8 @@
 				return -1;
 			}
 		}
-		if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL,
+		if (read_ref_at(get_main_ref_store(r),
+				real_ref, flags, at_time, nth, oid, NULL,
 				&co_time, &co_tz, &co_cnt)) {
 			if (!len) {
 				if (starts_with(real_ref, "refs/heads/")) {
@@ -893,18 +934,19 @@
 	return 0;
 }
 
-static enum get_oid_result get_parent(const char *name, int len,
+static enum get_oid_result get_parent(struct repository *r,
+				      const char *name, int len,
 				      struct object_id *result, int idx)
 {
 	struct object_id oid;
-	enum get_oid_result ret = get_oid_1(name, len, &oid,
+	enum get_oid_result ret = get_oid_1(r, name, len, &oid,
 					    GET_OID_COMMITTISH);
 	struct commit *commit;
 	struct commit_list *p;
 
 	if (ret)
 		return ret;
-	commit = lookup_commit_reference(the_repository, &oid);
+	commit = lookup_commit_reference(r, &oid);
 	if (parse_commit(commit))
 		return MISSING_OBJECT;
 	if (!idx) {
@@ -922,7 +964,8 @@
 	return MISSING_OBJECT;
 }
 
-static enum get_oid_result get_nth_ancestor(const char *name, int len,
+static enum get_oid_result get_nth_ancestor(struct repository *r,
+					    const char *name, int len,
 					    struct object_id *result,
 					    int generation)
 {
@@ -930,10 +973,10 @@
 	struct commit *commit;
 	int ret;
 
-	ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH);
+	ret = get_oid_1(r, name, len, &oid, GET_OID_COMMITTISH);
 	if (ret)
 		return ret;
-	commit = lookup_commit_reference(the_repository, &oid);
+	commit = lookup_commit_reference(r, &oid);
 	if (!commit)
 		return MISSING_OBJECT;
 
@@ -946,20 +989,20 @@
 	return FOUND;
 }
 
-struct object *peel_to_type(const char *name, int namelen,
-			    struct object *o, enum object_type expected_type)
+struct object *repo_peel_to_type(struct repository *r, const char *name, int namelen,
+				 struct object *o, enum object_type expected_type)
 {
 	if (name && !namelen)
 		namelen = strlen(name);
 	while (1) {
-		if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
+		if (!o || (!o->parsed && !parse_object(r, &o->oid)))
 			return NULL;
 		if (expected_type == OBJ_ANY || o->type == expected_type)
 			return o;
 		if (o->type == OBJ_TAG)
 			o = ((struct tag*) o)->tagged;
 		else if (o->type == OBJ_COMMIT)
-			o = &(get_commit_tree(((struct commit *)o))->object);
+			o = &(repo_get_commit_tree(r, ((struct commit *)o))->object);
 		else {
 			if (name)
 				error("%.*s: expected %s type, but the object "
@@ -971,8 +1014,8 @@
 	}
 }
 
-static int peel_onion(const char *name, int len, struct object_id *oid,
-		      unsigned lookup_flags)
+static int peel_onion(struct repository *r, const char *name, int len,
+		      struct object_id *oid, unsigned lookup_flags)
 {
 	struct object_id outer;
 	const char *sp;
@@ -1022,15 +1065,15 @@
 	else if (expected_type == OBJ_TREE)
 		lookup_flags |= GET_OID_TREEISH;
 
-	if (get_oid_1(name, sp - name - 2, &outer, lookup_flags))
+	if (get_oid_1(r, name, sp - name - 2, &outer, lookup_flags))
 		return -1;
 
-	o = parse_object(the_repository, &outer);
+	o = parse_object(r, &outer);
 	if (!o)
 		return -1;
 	if (!expected_type) {
-		o = deref_tag(the_repository, o, name, sp - name - 2);
-		if (!o || (!o->parsed && !parse_object(the_repository, &o->oid)))
+		o = deref_tag(r, o, name, sp - name - 2);
+		if (!o || (!o->parsed && !parse_object(r, &o->oid)))
 			return -1;
 		oidcpy(oid, &o->oid);
 		return 0;
@@ -1041,7 +1084,7 @@
 	 * if we do not get the needed object, we should
 	 * barf.
 	 */
-	o = peel_to_type(name, len, o, expected_type);
+	o = repo_peel_to_type(r, name, len, o, expected_type);
 	if (!o)
 		return -1;
 
@@ -1061,14 +1104,16 @@
 
 		prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
 		commit_list_insert((struct commit *)o, &list);
-		ret = get_oid_oneline(prefix, oid, list);
+		ret = get_oid_oneline(r, prefix, oid, list);
 		free(prefix);
 		return ret;
 	}
 	return 0;
 }
 
-static int get_describe_name(const char *name, int len, struct object_id *oid)
+static int get_describe_name(struct repository *r,
+			     const char *name, int len,
+			     struct object_id *oid)
 {
 	const char *cp;
 	unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT;
@@ -1082,14 +1127,16 @@
 			if (ch == 'g' && cp[-1] == '-') {
 				cp++;
 				len -= cp - name;
-				return get_short_oid(cp, len, oid, flags);
+				return get_short_oid(r,
+						     cp, len, oid, flags);
 			}
 		}
 	}
 	return -1;
 }
 
-static enum get_oid_result get_oid_1(const char *name, int len,
+static enum get_oid_result get_oid_1(struct repository *r,
+				     const char *name, int len,
 				     struct object_id *oid,
 				     unsigned lookup_flags)
 {
@@ -1118,25 +1165,25 @@
 		if (!num && len1 == len - 1)
 			num = 1;
 		if (has_suffix == '^')
-			return get_parent(name, len1, oid, num);
+			return get_parent(r, name, len1, oid, num);
 		/* else if (has_suffix == '~') -- goes without saying */
-		return get_nth_ancestor(name, len1, oid, num);
+		return get_nth_ancestor(r, name, len1, oid, num);
 	}
 
-	ret = peel_onion(name, len, oid, lookup_flags);
+	ret = peel_onion(r, name, len, oid, lookup_flags);
 	if (!ret)
 		return FOUND;
 
-	ret = get_oid_basic(name, len, oid, lookup_flags);
+	ret = get_oid_basic(r, name, len, oid, lookup_flags);
 	if (!ret)
 		return FOUND;
 
 	/* It could be describe output that is "SOMETHING-gXXXX" */
-	ret = get_describe_name(name, len, oid);
+	ret = get_describe_name(r, name, len, oid);
 	if (!ret)
 		return FOUND;
 
-	return get_short_oid(name, len, oid, lookup_flags);
+	return get_short_oid(r, name, len, oid, lookup_flags);
 }
 
 /*
@@ -1155,15 +1202,21 @@
 /* Remember to update object flag allocation in object.h */
 #define ONELINE_SEEN (1u<<20)
 
+struct handle_one_ref_cb {
+	struct repository *repo;
+	struct commit_list **list;
+};
+
 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(the_repository, oid);
+	struct handle_one_ref_cb *cb = cb_data;
+	struct commit_list **list = cb->list;
+	struct object *object = parse_object(cb->repo, oid);
 	if (!object)
 		return 0;
 	if (object->type == OBJ_TAG) {
-		object = deref_tag(the_repository, object, path,
+		object = deref_tag(cb->repo, object, path,
 				   strlen(path));
 		if (!object)
 			return 0;
@@ -1174,8 +1227,9 @@
 	return 0;
 }
 
-static int get_oid_oneline(const char *prefix, struct object_id *oid,
-			    struct commit_list *list)
+static int get_oid_oneline(struct repository *r,
+			   const char *prefix, struct object_id *oid,
+			   struct commit_list *list)
 {
 	struct commit_list *backup = NULL, *l;
 	int found = 0;
@@ -1206,7 +1260,7 @@
 		int matches;
 
 		commit = pop_most_recent_commit(&list, ONELINE_SEEN);
-		if (!parse_object(the_repository, &commit->object.oid))
+		if (!parse_object(r, &commit->object.oid))
 			continue;
 		buf = get_commit_buffer(commit, NULL);
 		p = strstr(buf, "\n\n");
@@ -1258,7 +1312,8 @@
  * Parse @{-N} syntax, return the number of characters parsed
  * if successful; otherwise signal an error with negative value.
  */
-static int interpret_nth_prior_checkout(const char *name, int namelen,
+static int interpret_nth_prior_checkout(struct repository *r,
+					const char *name, int namelen,
 					struct strbuf *buf)
 {
 	long nth;
@@ -1282,18 +1337,22 @@
 	cb.remaining = nth;
 	strbuf_init(&cb.buf, 20);
 
-	retval = 0;
-	if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
+	retval = refs_for_each_reflog_ent_reverse(get_main_ref_store(r),
+			"HEAD", grab_nth_branch_switch, &cb);
+	if (0 < retval) {
 		strbuf_reset(buf);
 		strbuf_addbuf(buf, &cb.buf);
 		retval = brace - name + 1;
-	}
+	} else
+		retval = 0;
 
 	strbuf_release(&cb.buf);
 	return retval;
 }
 
-int get_oid_mb(const char *name, struct object_id *oid)
+int repo_get_oid_mb(struct repository *r,
+		    const char *name,
+		    struct object_id *oid)
 {
 	struct commit *one, *two;
 	struct commit_list *mbs;
@@ -1303,27 +1362,29 @@
 
 	dots = strstr(name, "...");
 	if (!dots)
-		return get_oid(name, oid);
+		return repo_get_oid(r, name, oid);
 	if (dots == name)
-		st = get_oid("HEAD", &oid_tmp);
+		st = repo_get_oid(r, "HEAD", &oid_tmp);
 	else {
 		struct strbuf sb;
 		strbuf_init(&sb, dots - name);
 		strbuf_add(&sb, name, dots - name);
-		st = get_oid_committish(sb.buf, &oid_tmp);
+		st = repo_get_oid_committish(r, sb.buf, &oid_tmp);
 		strbuf_release(&sb);
 	}
 	if (st)
 		return st;
-	one = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
+	one = lookup_commit_reference_gently(r, &oid_tmp, 0);
 	if (!one)
 		return -1;
 
-	if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
+	if (repo_get_oid_committish(r, dots[3] ? (dots + 3) : "HEAD", &oid_tmp))
 		return -1;
-	two = lookup_commit_reference_gently(the_repository, &oid_tmp, 0);
+	two = lookup_commit_reference_gently(r, &oid_tmp, 0);
 	if (!two)
 		return -1;
+	if (r != the_repository)
+		BUG("sorry get_merge_bases() can't take struct repository yet");
 	mbs = get_merge_bases(one, two);
 	if (!mbs || mbs->next)
 		st = -1;
@@ -1357,7 +1418,8 @@
 	return 1;
 }
 
-static int reinterpret(const char *name, int namelen, int len,
+static int reinterpret(struct repository *r,
+		       const char *name, int namelen, int len,
 		       struct strbuf *buf, unsigned allowed)
 {
 	/* we have extra data, which might need further processing */
@@ -1366,7 +1428,7 @@
 	int ret;
 
 	strbuf_add(buf, name + len, namelen - len);
-	ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
+	ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, allowed);
 	/* that data was not interpreted, remove our cruft */
 	if (ret < 0) {
 		strbuf_setlen(buf, used);
@@ -1379,9 +1441,9 @@
 	return ret - used + len;
 }
 
-static void set_shortened_ref(struct strbuf *buf, const char *ref)
+static void set_shortened_ref(struct repository *r, struct strbuf *buf, const char *ref)
 {
-	char *s = shorten_unambiguous_ref(ref, 0);
+	char *s = refs_shorten_unambiguous_ref(get_main_ref_store(r), ref, 0);
 	strbuf_reset(buf);
 	strbuf_addstr(buf, s);
 	free(s);
@@ -1402,7 +1464,8 @@
 	return 0;
 }
 
-static int interpret_branch_mark(const char *name, int namelen,
+static int interpret_branch_mark(struct repository *r,
+				 const char *name, int namelen,
 				 int at, struct strbuf *buf,
 				 int (*get_mark)(const char *, int),
 				 const char *(*get_data)(struct branch *,
@@ -1435,12 +1498,14 @@
 	if (!branch_interpret_allowed(value, allowed))
 		return -1;
 
-	set_shortened_ref(buf, value);
+	set_shortened_ref(r, buf, value);
 	return len + at;
 }
 
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
-			  unsigned allowed)
+int repo_interpret_branch_name(struct repository *r,
+			       const char *name, int namelen,
+			       struct strbuf *buf,
+			       unsigned allowed)
 {
 	char *at;
 	const char *start;
@@ -1450,14 +1515,14 @@
 		namelen = strlen(name);
 
 	if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
-		len = interpret_nth_prior_checkout(name, namelen, buf);
+		len = interpret_nth_prior_checkout(r, name, namelen, buf);
 		if (!len) {
 			return len; /* syntax Ok, not enough switches */
 		} else if (len > 0) {
 			if (len == namelen)
 				return len; /* consumed all */
 			else
-				return reinterpret(name, namelen, len, buf, allowed);
+				return reinterpret(r, name, namelen, len, buf, allowed);
 		}
 	}
 
@@ -1468,17 +1533,17 @@
 		if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
 			len = interpret_empty_at(name, namelen, at - name, buf);
 			if (len > 0)
-				return reinterpret(name, namelen, len, buf,
+				return reinterpret(r, name, namelen, len, buf,
 						   allowed);
 		}
 
-		len = interpret_branch_mark(name, namelen, at - name, buf,
+		len = interpret_branch_mark(r, name, namelen, at - name, buf,
 					    upstream_mark, branch_get_upstream,
 					    allowed);
 		if (len > 0)
 			return len;
 
-		len = interpret_branch_mark(name, namelen, at - name, buf,
+		len = interpret_branch_mark(r, name, namelen, at - name, buf,
 					    push_mark, branch_get_push,
 					    allowed);
 		if (len > 0)
@@ -1524,10 +1589,10 @@
  * This is like "get_oid_basic()", except it allows "object ID expressions",
  * notably "xyz^" for "parent of xyz"
  */
-int get_oid(const char *name, struct object_id *oid)
+int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository, name, 0, oid, &unused);
+	return get_oid_with_context(r, name, 0, oid, &unused);
 }
 
 /*
@@ -1560,43 +1625,48 @@
  * commit-ish. It is merely to give a hint to the disambiguation
  * machinery.
  */
-int get_oid_committish(const char *name, struct object_id *oid)
+int repo_get_oid_committish(struct repository *r,
+			    const char *name,
+			    struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository,
-				    name, GET_OID_COMMITTISH,
+	return get_oid_with_context(r, name, GET_OID_COMMITTISH,
 				    oid, &unused);
 }
 
-int get_oid_treeish(const char *name, struct object_id *oid)
+int repo_get_oid_treeish(struct repository *r,
+			 const char *name,
+			 struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository,
-				    name, GET_OID_TREEISH,
+	return get_oid_with_context(r, name, GET_OID_TREEISH,
 				    oid, &unused);
 }
 
-int get_oid_commit(const char *name, struct object_id *oid)
+int repo_get_oid_commit(struct repository *r,
+			const char *name,
+			struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository,
-				    name, GET_OID_COMMIT,
+	return get_oid_with_context(r, name, GET_OID_COMMIT,
 				    oid, &unused);
 }
 
-int get_oid_tree(const char *name, struct object_id *oid)
+int repo_get_oid_tree(struct repository *r,
+		      const char *name,
+		      struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository,
-				    name, GET_OID_TREE,
+	return get_oid_with_context(r, name, GET_OID_TREE,
 				    oid, &unused);
 }
 
-int get_oid_blob(const char *name, struct object_id *oid)
+int repo_get_oid_blob(struct repository *r,
+		      const char *name,
+		      struct object_id *oid)
 {
 	struct object_context unused;
-	return get_oid_with_context(the_repository,
-				    name, GET_OID_BLOB,
+	return get_oid_with_context(r, name, GET_OID_BLOB,
 				    oid, &unused);
 }
 
@@ -1608,7 +1678,7 @@
 				      int object_name_len)
 {
 	struct object_id oid;
-	unsigned mode;
+	unsigned short mode;
 
 	if (!prefix)
 		prefix = "";
@@ -1635,11 +1705,12 @@
 }
 
 /* Must be called only when :stage:filename doesn't exist. */
-static void diagnose_invalid_index_path(struct index_state *istate,
+static void diagnose_invalid_index_path(struct repository *r,
 					int stage,
 					const char *prefix,
 					const char *filename)
 {
+	struct index_state *istate = r->index;
 	const struct cache_entry *ce;
 	int pos;
 	unsigned namelen = strlen(filename);
@@ -1679,7 +1750,7 @@
 			    ce_stage(ce), filename);
 	}
 
-	if (file_exists(filename))
+	if (repo_file_exists(r, filename))
 		die("Path '%s' exists on disk, but not in the index.", filename);
 	if (is_missing_file_error(errno))
 		die("Path '%s' does not exist (neither on disk nor in the index).",
@@ -1689,12 +1760,12 @@
 }
 
 
-static char *resolve_relative_path(const char *rel)
+static char *resolve_relative_path(struct repository *r, const char *rel)
 {
 	if (!starts_with(rel, "./") && !starts_with(rel, "../"))
 		return NULL;
 
-	if (!is_inside_work_tree())
+	if (r != the_repository || !is_inside_work_tree())
 		die("relative path syntax can't be used outside working tree.");
 
 	/* die() inside prefix_path() if resolved path is outside worktree */
@@ -1721,7 +1792,7 @@
 	memset(oc, 0, sizeof(*oc));
 	oc->mode = S_IFINVALID;
 	strbuf_init(&oc->symlink_path, 0);
-	ret = get_oid_1(name, namelen, oid, flags);
+	ret = get_oid_1(repo, name, namelen, oid, flags);
 	if (!ret)
 		return ret;
 	/*
@@ -1737,12 +1808,15 @@
 		char *new_path = NULL;
 		int pos;
 		if (!only_to_die && namelen > 2 && name[1] == '/') {
+			struct handle_one_ref_cb cb;
 			struct commit_list *list = NULL;
 
-			for_each_ref(handle_one_ref, &list);
-			head_ref(handle_one_ref, &list);
+			cb.repo = repo;
+			cb.list = &list;
+			refs_for_each_ref(repo->refs, handle_one_ref, &cb);
+			refs_head_ref(repo->refs, handle_one_ref, &cb);
 			commit_list_sort_by_date(&list);
-			return get_oid_oneline(name + 2, oid, list);
+			return get_oid_oneline(repo, name + 2, oid, list);
 		}
 		if (namelen < 3 ||
 		    name[2] != ':' ||
@@ -1752,7 +1826,7 @@
 			stage = name[1] - '0';
 			cp = name + 3;
 		}
-		new_path = resolve_relative_path(cp);
+		new_path = resolve_relative_path(repo, cp);
 		if (!new_path) {
 			namelen = namelen - (cp - name);
 		} else {
@@ -1764,7 +1838,7 @@
 			oc->path = xstrdup(cp);
 
 		if (!repo->index->cache)
-			repo_read_index(the_repository);
+			repo_read_index(repo);
 		pos = index_name_pos(repo->index, cp, namelen);
 		if (pos < 0)
 			pos = -pos - 1;
@@ -1782,7 +1856,7 @@
 			pos++;
 		}
 		if (only_to_die && name[1] && name[1] != '/')
-			diagnose_invalid_index_path(repo->index, stage, prefix, cp);
+			diagnose_invalid_index_path(repo, stage, prefix, cp);
 		free(new_path);
 		return -1;
 	}
@@ -1802,13 +1876,21 @@
 		sub_flags &= ~GET_OID_DISAMBIGUATORS;
 		sub_flags |= GET_OID_TREEISH;
 
-		if (!get_oid_1(name, len, &tree_oid, sub_flags)) {
+		if (!get_oid_1(repo, name, len, &tree_oid, sub_flags)) {
 			const char *filename = cp+1;
 			char *new_filename = NULL;
 
-			new_filename = resolve_relative_path(filename);
+			new_filename = resolve_relative_path(repo, filename);
 			if (new_filename)
 				filename = new_filename;
+			/*
+			 * NEEDSWORK: Eventually get_tree_entry*() should
+			 * learn to take struct repository directly and we
+			 * would not need to inject submodule odb to the
+			 * in-core odb.
+			 */
+			if (repo != the_repository)
+				add_to_alternates_memory(repo->objects->odb->path);
 			if (flags & GET_OID_FOLLOW_SYMLINKS) {
 				ret = get_tree_entry_follow_symlinks(&tree_oid,
 					filename, oid, &oc->symlink_path,
@@ -1843,11 +1925,13 @@
  * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
  * you have a chance to diagnose the error further.
  */
-void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
+void maybe_die_on_misspelt_object_name(struct repository *r,
+				       const char *name,
+				       const char *prefix)
 {
 	struct object_context oc;
 	struct object_id oid;
-	get_oid_with_context_1(the_repository, name, GET_OID_ONLY_TO_DIE,
+	get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE,
 			       prefix, &oid, &oc);
 }
 
diff --git a/streaming.h b/streaming.h
index 32f4626..f465a3c 100644
--- a/streaming.h
+++ b/streaming.h
@@ -8,10 +8,10 @@
 /* opaque */
 struct git_istream;
 
-extern struct git_istream *open_istream(const struct object_id *, enum object_type *, unsigned long *, struct stream_filter *);
-extern int close_istream(struct git_istream *);
-extern ssize_t read_istream(struct git_istream *, void *, size_t);
+struct git_istream *open_istream(const struct object_id *, enum object_type *, unsigned long *, struct stream_filter *);
+int close_istream(struct git_istream *);
+ssize_t read_istream(struct git_istream *, void *, size_t);
 
-extern int stream_blob_to_fd(int fd, const struct object_id *, struct stream_filter *, int can_seek);
+int stream_blob_to_fd(int fd, const struct object_id *, struct stream_filter *, int can_seek);
 
 #endif /* STREAMING_H */
diff --git a/string-list.h b/string-list.h
index 18c718c..f964399 100644
--- a/string-list.h
+++ b/string-list.h
@@ -174,8 +174,8 @@
  * Remove the given string from the sorted list.  If the string
  * doesn't exist, the list is not altered.
  */
-extern void string_list_remove(struct string_list *list, const char *string,
-			       int free_util);
+void string_list_remove(struct string_list *list, const char *string,
+			int free_util);
 
 /**
  * Check if the given string is part of a sorted list. If it is part of the list,
diff --git a/sub-process.h b/sub-process.h
index 71b18ad..5c182fa 100644
--- a/sub-process.h
+++ b/sub-process.h
@@ -42,10 +42,10 @@
 /* subprocess functions */
 
 /* Function to test two subprocess hashmap entries for equality. */
-extern int cmd2process_cmp(const void *unused_cmp_data,
-			   const void *e1,
-			   const void *e2,
-			   const void *unused_keydata);
+int cmd2process_cmp(const void *unused_cmp_data,
+		    const void *e1,
+		    const void *e2,
+		    const void *unused_keydata);
 
 /*
  * User-supplied function to initialize the sub-process.  This is
diff --git a/submodule-config.c b/submodule-config.c
index 66653e8..4264ee2 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -625,23 +625,16 @@
 		const struct config_options opts = { 0 };
 		struct object_id oid;
 		char *file;
+		char *oidstr = NULL;
 
 		file = repo_worktree_path(repo, GITMODULES_FILE);
 		if (file_exists(file)) {
 			config_source.file = file;
-		} else if (repo->submodule_prefix) {
-			/*
-			 * When get_oid and config_with_options, used below,
-			 * become able to work on a specific repository, this
-			 * warning branch can be removed.
-			 */
-			warning("nested submodules without %s in the working tree are not supported yet",
-				GITMODULES_FILE);
-			goto out;
-		} else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
-			config_source.blob = GITMODULES_INDEX;
-		} else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
-			config_source.blob = GITMODULES_HEAD;
+		} else if (repo_get_oid(repo, GITMODULES_INDEX, &oid) >= 0 ||
+			   repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) {
+			config_source.blob = oidstr = xstrdup(oid_to_hex(&oid));
+			if (repo != the_repository)
+				add_to_alternates_memory(repo->objects->odb->path);
 		} else {
 			goto out;
 		}
@@ -649,6 +642,7 @@
 		config_with_options(fn, data, &config_source, &opts);
 
 out:
+		free(oidstr);
 		free(file);
 	}
 }
diff --git a/submodule-config.h b/submodule-config.h
index 4dc9b07..1b4e2da 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -30,17 +30,17 @@
 struct submodule_cache;
 struct repository;
 
-extern void submodule_cache_free(struct submodule_cache *cache);
+void submodule_cache_free(struct submodule_cache *cache);
 
-extern int parse_submodule_fetchjobs(const char *var, const char *value);
-extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+int parse_submodule_fetchjobs(const char *var, const char *value);
+int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 struct option;
-extern int option_fetch_parse_recurse_submodules(const struct option *opt,
-						 const char *arg, int unset);
-extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
-extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
-extern void repo_read_gitmodules(struct repository *repo);
-extern void gitmodules_config_oid(const struct object_id *commit_oid);
+int option_fetch_parse_recurse_submodules(const struct option *opt,
+					  const char *arg, int unset);
+int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
+int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
+void repo_read_gitmodules(struct repository *repo);
+void gitmodules_config_oid(const struct object_id *commit_oid);
 const struct submodule *submodule_from_name(struct repository *r,
 					    const struct object_id *commit_or_tree,
 					    const char *name);
@@ -66,7 +66,7 @@
  * New helpers to retrieve arbitrary configuration from the '.gitmodules' file
  * should NOT be added.
  */
-extern void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
-extern void update_clone_config_from_gitmodules(int *max_jobs);
+void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
+void update_clone_config_from_gitmodules(int *max_jobs);
 
 #endif /* SUBMODULE_CONFIG_H */
diff --git a/submodule.c b/submodule.c
index b16c0ec..2cfaba0 100644
--- a/submodule.c
+++ b/submodule.c
@@ -994,7 +994,7 @@
 		if (start_command(&cp))
 			die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s",
 					path);
-		if (strbuf_read(&buf, cp.out, 41))
+		if (strbuf_read(&buf, cp.out, the_hash_algo->hexsz + 1))
 			needs_pushing = 1;
 		finish_command(&cp);
 		close(cp.out);
diff --git a/t/README b/t/README
index af1243e..6404f33 100644
--- a/t/README
+++ b/t/README
@@ -871,6 +871,26 @@
 		...
 	'
 
+ - test_atexit <script>
+
+   Prepend <script> to a list of commands to run unconditionally to
+   clean up before the test script exits, e.g. to stop a daemon:
+
+	test_expect_success 'test git daemon' '
+		git daemon &
+		daemon_pid=$! &&
+		test_atexit 'kill $daemon_pid' &&
+		hello world
+	'
+
+   The commands will be executed before the trash directory is removed,
+   i.e. the atexit commands will still be able to access any pidfiles or
+   socket files.
+
+   Note that these commands will be run even when a test script run
+   with '--immediate' fails.  Be careful with your atexit commands to
+   minimize any changes to the failed state.
+
  - test_write_lines <lines>
 
    Write <lines> on standard output, one line per argument.
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 6da48a2..d933af5 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -68,6 +68,14 @@
 	check_count A 2
 '
 
+test_expect_success 'blame in a bare repo without starting commit' '
+	git clone --bare . bare.git &&
+	(
+		cd bare.git &&
+		check_count A 2
+	)
+'
+
 test_expect_success 'blame by tag objects' '
 	git tag -m "test tag" testTag &&
 	git tag -m "test tag #2" testTag2 testTag &&
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index b325380..585347e 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -55,7 +55,7 @@
 	}
 }
 
-static void parse_dates(const char **argv, struct timeval *now)
+static void parse_dates(const char **argv)
 {
 	struct strbuf result = STRBUF_INIT;
 
@@ -124,7 +124,7 @@
 	else if (skip_prefix(*argv, "show:", &x))
 		show_dates(argv+1, x);
 	else if (!strcmp(*argv, "parse"))
-		parse_dates(argv+1, &now);
+		parse_dates(argv+1);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "timestamp"))
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index cc88fba..2232b2f 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -132,7 +132,7 @@
 		OPT_NOOP_NOARG(0, "obsolete"),
 		OPT_STRING_LIST(0, "list", &list, "str", "add str to list"),
 		OPT_GROUP("Magic arguments"),
-		OPT_ARGUMENT("quux", "means --quux"),
+		OPT_ARGUMENT("quux", NULL, "means --quux"),
 		OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
 			number_callback),
 		{ OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b",
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
index 5bc9c46..f402844 100644
--- a/t/helper/test-prio-queue.c
+++ b/t/helper/test-prio-queue.c
@@ -40,7 +40,7 @@
 		} else if (!strcmp(*argv, "stack")) {
 			pq.compare = NULL;
 		} else {
-			int *v = malloc(sizeof(*v));
+			int *v = xmalloc(sizeof(*v));
 			*v = atoi(*argv);
 			prio_queue_put(&pq, v);
 		}
diff --git a/builtin/serve.c b/t/helper/test-serve-v2.c
similarity index 80%
rename from builtin/serve.c
rename to t/helper/test-serve-v2.c
index d3fd240..aee35e5 100644
--- a/builtin/serve.c
+++ b/t/helper/test-serve-v2.c
@@ -1,14 +1,14 @@
+#include "test-tool.h"
 #include "cache.h"
-#include "builtin.h"
 #include "parse-options.h"
 #include "serve.h"
 
 static char const * const serve_usage[] = {
-	N_("git serve [<options>]"),
+	N_("test-tool serve-v2 [<options>]"),
 	NULL
 };
 
-int cmd_serve(int argc, const char **argv, const char *prefix)
+int cmd__serve_v2(int argc, const char **argv)
 {
 	struct serve_options opts = SERVE_OPTIONS_INIT;
 
@@ -19,6 +19,7 @@
 			 N_("exit immediately after advertising capabilities")),
 		OPT_END()
 	};
+	const char *prefix = setup_git_directory();
 
 	/* ignore all unknown cmdline switches for now */
 	argc = parse_options(argc, argv, prefix, options, serve_usage,
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 53c0693..087a8c0 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -1,6 +1,12 @@
 #include "git-compat-util.h"
 #include "test-tool.h"
 #include "trace2.h"
+#include "parse-options.h"
+
+static const char * const test_tool_usage[] = {
+	"test-tool [-C <directory>] <command [<arguments>...]]",
+	NULL
+};
 
 struct test_cmd {
 	const char *name;
@@ -43,6 +49,7 @@
 	{ "revision-walking", cmd__revision_walking },
 	{ "run-command", cmd__run_command },
 	{ "scrap-cache-tree", cmd__scrap_cache_tree },
+	{ "serve-v2", cmd__serve_v2 },
 	{ "sha1", cmd__sha1 },
 	{ "sha1-array", cmd__sha1_array },
 	{ "sha256", cmd__sha256 },
@@ -75,11 +82,24 @@
 int cmd_main(int argc, const char **argv)
 {
 	int i;
+	const char *working_directory = NULL;
+	struct option options[] = {
+		OPT_STRING('C', NULL, &working_directory, "directory",
+			   "change the working directory"),
+		OPT_END()
+	};
 
 	BUG_exit_code = 99;
+	argc = parse_options(argc, argv, NULL, options, test_tool_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION |
+			     PARSE_OPT_KEEP_ARGV0);
+
 	if (argc < 2)
 		die_usage();
 
+	if (working_directory && chdir(working_directory) < 0)
+		die("Could not cd to '%s'", working_directory);
+
 	for (i = 0; i < ARRAY_SIZE(cmds); i++) {
 		if (!strcmp(cmds[i].name, argv[1])) {
 			argv++;
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index ffab4d1..7e703f3 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -39,6 +39,7 @@
 int cmd__revision_walking(int argc, const char **argv);
 int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
+int cmd__serve_v2(int argc, const char **argv);
 int cmd__sha1(int argc, const char **argv);
 int cmd__sha1_array(int argc, const char **argv);
 int cmd__sha256(int argc, const char **argv);
diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh
index 1daf694..4d22e42 100755
--- a/t/interop/i5500-git-daemon.sh
+++ b/t/interop/i5500-git-daemon.sh
@@ -37,5 +37,4 @@
 	test_cmp expect actual
 '
 
-stop_git_daemon
 test_done
diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh
index 79db3b7..7b34071 100644
--- a/t/lib-git-daemon.sh
+++ b/t/lib-git-daemon.sh
@@ -13,7 +13,6 @@
 #
 #	test_expect_success ...
 #
-#	stop_git_daemon
 #	test_done
 
 test_tristate GIT_TEST_GIT_DAEMON
@@ -31,10 +30,12 @@
 test_set_port LIB_GIT_DAEMON_PORT
 
 GIT_DAEMON_PID=
+GIT_DAEMON_PIDFILE="$PWD"/daemon.pid
 GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
 GIT_DAEMON_HOST_PORT=127.0.0.1:$LIB_GIT_DAEMON_PORT
 GIT_DAEMON_URL=git://$GIT_DAEMON_HOST_PORT
 
+registered_stop_git_daemon_atexit_handler=
 start_git_daemon() {
 	if test -n "$GIT_DAEMON_PID"
 	then
@@ -43,13 +44,19 @@
 
 	mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
 
-	trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+	# One of the test scripts stops and then re-starts 'git daemon'.
+	# Don't register and then run the same atexit handlers several times.
+	if test -z "$registered_stop_git_daemon_atexit_handler"
+	then
+		test_atexit 'stop_git_daemon'
+		registered_stop_git_daemon_atexit_handler=AlreadyDone
+	fi
 
 	say >&3 "Starting git daemon ..."
 	mkfifo git_daemon_output
 	${LIB_GIT_DAEMON_COMMAND:-git daemon} \
 		--listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
-		--reuseaddr --verbose \
+		--reuseaddr --verbose --pid-file="$GIT_DAEMON_PIDFILE" \
 		--base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
 		"$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
 		>&3 2>git_daemon_output &
@@ -65,7 +72,7 @@
 	then
 		kill "$GIT_DAEMON_PID"
 		wait "$GIT_DAEMON_PID"
-		trap 'die' EXIT
+		unset GIT_DAEMON_PID
 		test_skip_or_die $GIT_TEST_GIT_DAEMON \
 			"git daemon failed to start"
 	fi
@@ -77,8 +84,6 @@
 		return
 	fi
 
-	trap 'die' EXIT
-
 	# kill git-daemon child of git
 	say >&3 "Stopping git daemon ..."
 	kill "$GIT_DAEMON_PID"
@@ -88,8 +93,9 @@
 	then
 		error "git daemon exited with status: $ret"
 	fi
+	kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
 	GIT_DAEMON_PID=
-	rm -f git_daemon_output
+	rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
 }
 
 # A stripped-down version of a netcat client, that connects to a "host:port"
diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh
index b3be3ba..547b9f8 100644
--- a/t/lib-git-p4.sh
+++ b/t/lib-git-p4.sh
@@ -44,15 +44,6 @@
 	echo "$path"
 }
 
-# On Solaris the 'date +%s' function is not supported and therefore we
-# need this replacement.
-# Attention: This function is not safe again against time offset updates
-# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
-# function could fix that but it is not in Python until 3.3.
-time_in_seconds () {
-	(cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
-}
-
 test_set_port P4DPORT
 
 P4PORT=localhost:$P4DPORT
@@ -67,14 +58,9 @@
 git="$TRASH_DIRECTORY/git"
 pidfile="$TRASH_DIRECTORY/p4d.pid"
 
-# Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup () {
-	if test -f "$pidfile"
-	then
-		kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
-	fi
+stop_p4d_and_watchdog () {
+	kill -9 $p4d_pid $watchdog_pid
 }
-trap cleanup EXIT
 
 # git p4 submit generates a temp file, which will
 # not get cleaned up if the submission fails.  Don't
@@ -82,7 +68,16 @@
 TMPDIR="$TRASH_DIRECTORY"
 export TMPDIR
 
+registered_stop_p4d_atexit_handler=
 start_p4d () {
+	# One of the test scripts stops and then re-starts p4d.
+	# Don't register and then run the same atexit handlers several times.
+	if test -z "$registered_stop_p4d_atexit_handler"
+	then
+		test_atexit 'stop_p4d_and_watchdog'
+		registered_stop_p4d_atexit_handler=AlreadyDone
+	fi
+
 	mkdir -p "$db" "$cli" "$git" &&
 	rm -f "$pidfile" &&
 	(
@@ -92,6 +87,7 @@
 			echo $! >"$pidfile"
 		}
 	) &&
+	p4d_pid=$(cat "$pidfile")
 
 	# This gives p4d a long time to start up, as it can be
 	# quite slow depending on the machine.  Set this environment
@@ -99,18 +95,18 @@
 	# an automated test setup.  If the p4d process dies, that
 	# will be caught with the "kill -0" check below.
 	i=${P4D_START_PATIENCE:-300}
-	pid=$(cat "$pidfile")
 
-	timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+	nr_tries_left=$P4D_TIMEOUT
 	while true
 	do
-		if test $(time_in_seconds) -gt $timeout
+		if test $nr_tries_left -eq 0
 		then
-			kill -9 $pid
+			kill -9 $p4d_pid
 			exit 1
 		fi
 		sleep 1
-	done &
+		nr_tries_left=$(($nr_tries_left - 1))
+	done 2>/dev/null 4>&2 &
 	watchdog_pid=$!
 
 	ready=
@@ -123,7 +119,7 @@
 			break
 		fi
 		# fail if p4d died
-		kill -0 $pid 2>/dev/null || break
+		kill -0 $p4d_pid 2>/dev/null || break
 		echo waiting for p4d to start
 		sleep 1
 		i=$(( $i - 1 ))
@@ -163,29 +159,18 @@
 }
 
 retry_until_success () {
-	timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
-	until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+	nr_tries_left=$RETRY_TIMEOUT
+	until "$@" 2>/dev/null || test $nr_tries_left -eq 0
 	do
 		sleep 1
+		nr_tries_left=$(($nr_tries_left - 1))
 	done
 }
 
-retry_until_fail () {
-	timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
-	until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
-	do
-		sleep 1
-	done
-}
-
-kill_p4d () {
-	pid=$(cat "$pidfile")
-	retry_until_fail kill $pid
-	retry_until_fail kill -9 $pid
-	# complain if it would not die
-	test_must_fail kill $pid >/dev/null 2>&1 &&
-	rm -rf "$db" "$cli" "$pidfile" &&
-	retry_until_fail kill -9 $watchdog_pid
+stop_and_cleanup_p4d () {
+	kill -9 $p4d_pid $watchdog_pid
+	wait $p4d_pid
+	rm -rf "$db" "$cli" "$pidfile"
 }
 
 cleanup_git () {
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index f3b478c..c1271d6 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -76,11 +76,6 @@
 		LIB_HTTPD_SVN="$loc"
 		start_httpd
 		;;
-	*)
-		stop_httpd () {
-			: noop
-		}
-		;;
 	esac
 }
 
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index 0dfb48c..b3cc62b 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -14,7 +14,6 @@
 #
 #	test_expect_success ...
 #
-#	stop_httpd
 #	test_done
 #
 # Can be configured using the following variables.
@@ -176,7 +175,7 @@
 start_httpd() {
 	prepare_httpd >&3 2>&4
 
-	trap 'code=$?; stop_httpd; (exit $code); die' EXIT
+	test_atexit stop_httpd
 
 	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
 		-f "$TEST_PATH/apache.conf" $HTTPD_PARA \
@@ -184,15 +183,12 @@
 		>&3 2>&4
 	if test $? -ne 0
 	then
-		trap 'die' EXIT
 		cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
 		test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
 	fi
 }
 
 stop_httpd() {
-	trap 'die' EXIT
-
 	"$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
 		-f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 5b56b23..1dd17fc 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -139,7 +139,7 @@
 		git revert HEAD &&
 
 		git checkout -b invalid_sub1 add_sub1 &&
-		git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+		git update-index --cacheinfo 160000 $(test_oid numeric) sub1 &&
 		git commit -m "Invalid sub1 commit" &&
 		git checkout -b valid_sub1 &&
 		git revert HEAD &&
@@ -196,6 +196,7 @@
 # the submodule repo if it doesn't exist and configures the most problematic
 # settings for diff.ignoreSubmodules.
 prolog () {
+	test_oid_init &&
 	(test -d submodule_update_repo || create_lib_submodule_repo) &&
 	test_config_global diff.ignoreSubmodules all &&
 	test_config diff.ignoreSubmodules all
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
index ebf1724..3042a85 100755
--- a/t/perf/p0001-rev-list.sh
+++ b/t/perf/p0001-rev-list.sh
@@ -14,6 +14,24 @@
 	git rev-list --all --objects >/dev/null
 '
 
+test_perf 'rev-list --parents' '
+	git rev-list --parents HEAD >/dev/null
+'
+
+test_expect_success 'create dummy file' '
+	echo unlikely-to-already-be-there >dummy &&
+	git add dummy &&
+	git commit -m dummy
+'
+
+test_perf 'rev-list -- dummy' '
+	git rev-list HEAD -- dummy
+'
+
+test_perf 'rev-list --parents -- dummy' '
+	git rev-list --parents HEAD -- dummy
+'
+
 test_expect_success 'create new unreferenced commit' '
 	commit=$(git commit-tree HEAD^{tree} -p HEAD) &&
 	test_export commit
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
index 99bdb16..a9b3e11 100755
--- a/t/perf/p5302-pack-index.sh
+++ b/t/perf/p5302-pack-index.sh
@@ -13,35 +13,40 @@
 	export PACK
 '
 
-test_expect_success 'create target repositories' '
-	for repo in t1 t2 t3 t4 t5 t6
-	do
-		git init --bare $repo
-	done
-'
-
 test_perf 'index-pack 0 threads' '
-	GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK
 '
 
 test_perf 'index-pack 1 thread ' '
-	GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
 '
 
 test_perf 'index-pack 2 threads' '
-	GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git git index-pack --threads=2 --stdin < $PACK
 '
 
 test_perf 'index-pack 4 threads' '
-	GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git git index-pack --threads=4 --stdin < $PACK
 '
 
 test_perf 'index-pack 8 threads' '
-	GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git git index-pack --threads=8 --stdin < $PACK
 '
 
 test_perf 'index-pack default number of threads' '
-	GIT_DIR=t6 git index-pack --stdin < $PACK
+	rm -rf repo.git &&
+	git init --bare repo.git &&
+	GIT_DIR=repo.git git index-pack --stdin < $PACK
 '
 
 test_done
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index bb91dbb..6a3a425 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -12,8 +12,7 @@
 # We intentionally use the deprecated pack.writebitmaps
 # config so that we can test against older versions of git.
 test_expect_success 'setup bitmap config' '
-	git config pack.writebitmaps true &&
-	git config pack.writebitmaphashcache true
+	git config pack.writebitmaps true
 '
 
 test_perf 'repack to disk' '
diff --git a/t/perf/p5311-pack-bitmaps-fetch.sh b/t/perf/p5311-pack-bitmaps-fetch.sh
index b045759..47c3fd7 100755
--- a/t/perf/p5311-pack-bitmaps-fetch.sh
+++ b/t/perf/p5311-pack-bitmaps-fetch.sh
@@ -7,7 +7,6 @@
 
 test_expect_success 'create bitmapped server repo' '
 	git config pack.writebitmaps true &&
-	git config pack.writebitmaphashcache true &&
 	git repack -ad
 '
 
diff --git a/t/perf/p5600-partial-clone.sh b/t/perf/p5600-partial-clone.sh
new file mode 100755
index 0000000..3e04bd2
--- /dev/null
+++ b/t/perf/p5600-partial-clone.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='performance of partial clones'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_expect_success 'enable server-side config' '
+	git config uploadpack.allowFilter true &&
+	git config uploadpack.allowAnySHA1InWant true
+'
+
+test_perf 'clone without blobs' '
+	rm -rf bare.git &&
+	git clone --no-local --bare --filter=blob:none . bare.git
+'
+
+test_perf 'checkout of result' '
+	rm -rf worktree &&
+	mkdir -p worktree/.git &&
+	tar -C bare.git -cf - . | tar -C worktree/.git -xf - &&
+	git -C worktree config core.bare false &&
+	git -C worktree checkout -f
+'
+
+test_done
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index b656600..c03054c 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -825,6 +825,24 @@
 	EOF
 "
 
+test_expect_success 'test_atexit is run' "
+	test_must_fail run_sub_test_lib_test \
+		atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
+	test_expect_success 'tests clean up even after a failure' '
+		> ../../clean-atexit &&
+		test_atexit rm ../../clean-atexit &&
+		> ../../also-clean-atexit &&
+		test_atexit rm ../../also-clean-atexit &&
+		> ../../dont-clean-atexit &&
+		(exit 1)
+	'
+	test_done
+	EOF
+	test_path_is_file dont-clean-atexit &&
+	test_path_is_missing clean-atexit &&
+	test_path_is_missing also-clean-atexit
+"
+
 test_expect_success 'test_oid setup' '
 	test_oid_init
 '
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 03a0aed..71194a3 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -3,6 +3,11 @@
 test_description='test trace2 facility (normal target)'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_BRIEF
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 # Add t/helper directory to PATH so that we can use a relative
 # path to run nested instances of test-tool.exe (see 004child).
 # This helps with HEREDOC comparisons later.
@@ -15,11 +20,6 @@
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_BRIEF
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -80,6 +80,21 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'automatic filename' '
+	test_when_finished "rm -r traces actual expect" &&
+	mkdir traces &&
+	GIT_TR2="$(pwd)/traces" test-tool trace2 001return 0 &&
+	perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <"$(ls traces/*)" >actual &&
+	cat >expect <<-EOF &&
+		version $V
+		start _EXE_ trace2 001return 0
+		cmd_name trace2 (trace2)
+		exit elapsed:_TIME_ code:0
+		atexit elapsed:_TIME_ code:0
+	EOF
+	test_cmp expect actual
+'
+
 # Verb 002exit
 #
 # Explicit exit(code) from within cmd_<verb> propagates <code>.
@@ -132,4 +147,43 @@
 	test_cmp expect actual
 '
 
+sane_unset GIT_TR2_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, normal stream, return code 0' '
+	test_when_finished "rm trace.normal actual expect" &&
+	test_config_global trace2.normalBrief 1 &&
+	test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+	test-tool trace2 001return 0 &&
+	perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+	cat >expect <<-EOF &&
+		version $V
+		start _EXE_ trace2 001return 0
+		cmd_name trace2 (trace2)
+		exit elapsed:_TIME_ code:0
+		atexit elapsed:_TIME_ code:0
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'using global config with include' '
+	test_when_finished "rm trace.normal actual expect real.gitconfig" &&
+	test_config_global trace2.normalBrief 1 &&
+	test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
+	mv "$(pwd)/.gitconfig" "$(pwd)/real.gitconfig" &&
+	test_config_global include.path "$(pwd)/real.gitconfig" &&
+	test-tool trace2 001return 0 &&
+	perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+	cat >expect <<-EOF &&
+		version $V
+		start _EXE_ trace2 001return 0
+		cmd_name trace2 (trace2)
+		exit elapsed:_TIME_ code:0
+		atexit elapsed:_TIME_ code:0
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index 953e2f7..b501e86 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -3,6 +3,11 @@
 test_description='test trace2 facility (perf target)'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_PERF_BRIEF
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 # Add t/helper directory to PATH so that we can use a relative
 # path to run nested instances of test-tool.exe (see 004child).
 # This helps with HEREDOC comparisons later.
@@ -15,11 +20,6 @@
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_PERF_BRIEF
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -50,7 +50,7 @@
 	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
 	cat >expect <<-EOF &&
 		d0|main|version|||||$V
-		d0|main|start|||||_EXE_ trace2 001return 0
+		d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
 		d0|main|cmd_name|||||trace2 (trace2)
 		d0|main|exit||_T_ABS_|||code:0
 		d0|main|atexit||_T_ABS_|||code:0
@@ -64,7 +64,7 @@
 	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
 	cat >expect <<-EOF &&
 		d0|main|version|||||$V
-		d0|main|start|||||_EXE_ trace2 001return 1
+		d0|main|start||_T_ABS_|||_EXE_ trace2 001return 1
 		d0|main|cmd_name|||||trace2 (trace2)
 		d0|main|exit||_T_ABS_|||code:1
 		d0|main|atexit||_T_ABS_|||code:1
@@ -82,7 +82,7 @@
 	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
 	cat >expect <<-EOF &&
 		d0|main|version|||||$V
-		d0|main|start|||||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
+		d0|main|start||_T_ABS_|||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
 		d0|main|cmd_name|||||trace2 (trace2)
 		d0|main|error|||||hello world
 		d0|main|error|||||this is a test
@@ -128,15 +128,15 @@
 	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
 	cat >expect <<-EOF &&
 		d0|main|version|||||$V
-		d0|main|start|||||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
+		d0|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
 		d0|main|cmd_name|||||trace2 (trace2)
 		d0|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 004child test-tool trace2 001return 0
 		d1|main|version|||||$V
-		d1|main|start|||||_EXE_ trace2 004child test-tool trace2 001return 0
+		d1|main|start||_T_ABS_|||_EXE_ trace2 004child test-tool trace2 001return 0
 		d1|main|cmd_name|||||trace2 (trace2/trace2)
 		d1|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 001return 0
 		d2|main|version|||||$V
-		d2|main|start|||||_EXE_ trace2 001return 0
+		d2|main|start||_T_ABS_|||_EXE_ trace2 001return 0
 		d2|main|cmd_name|||||trace2 (trace2/trace2/trace2)
 		d2|main|exit||_T_ABS_|||code:0
 		d2|main|atexit||_T_ABS_|||code:0
@@ -150,4 +150,25 @@
 	test_cmp expect actual
 '
 
+sane_unset GIT_TR2_PERF_BRIEF
+
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success 'using global config, perf stream, return code 0' '
+	test_when_finished "rm trace.perf actual expect" &&
+	test_config_global trace2.perfBrief 1 &&
+	test_config_global trace2.perfTarget "$(pwd)/trace.perf" &&
+	test-tool trace2 001return 0 &&
+	perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+	cat >expect <<-EOF &&
+		d0|main|version|||||$V
+		d0|main|start||_T_ABS_|||_EXE_ trace2 001return 0
+		d0|main|cmd_name|||||trace2 (trace2)
+		d0|main|exit||_T_ABS_|||code:0
+		d0|main|atexit||_T_ABS_|||code:0
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0212-trace2-event.sh b/t/t0212-trace2-event.sh
index 028b6c5..59adae8 100755
--- a/t/t0212-trace2-event.sh
+++ b/t/t0212-trace2-event.sh
@@ -3,6 +3,11 @@
 test_description='test trace2 facility'
 . ./test-lib.sh
 
+# Turn off any inherited trace2 settings for this test.
+sane_unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
+sane_unset GIT_TR2_BARE
+sane_unset GIT_TR2_CONFIG_PARAMS
+
 perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP
 
 # Add t/helper directory to PATH so that we can use a relative
@@ -17,11 +22,6 @@
 # Warning: So you may see extra lines in artifact files when
 # Warning: interactively debugging.
 
-# Turn off any inherited trace2 settings for this test.
-unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT
-unset GIT_TR2_BARE
-unset GIT_TR2_CONFIG_PARAMS
-
 V=$(git version | sed -e 's/^git version //') && export V
 
 # There are multiple trace2 targets: normal, perf, and event.
@@ -233,4 +233,36 @@
 	test_cmp expect actual
 '
 
+# Now test without environment variables and get all Trace2 settings
+# from the global config.
+
+test_expect_success JSON_PP 'using global config, event stream, error event' '
+	test_when_finished "rm trace.event actual expect" &&
+	test_config_global trace2.eventTarget "$(pwd)/trace.event" &&
+	test-tool trace2 003error "hello world" "this is a test" &&
+	perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+	sed -e "s/^|//" >expect <<-EOF &&
+	|VAR1 = {
+	|  "_SID0_":{
+	|    "argv":[
+	|      "_EXE_",
+	|      "trace2",
+	|      "003error",
+	|      "hello world",
+	|      "this is a test"
+	|    ],
+	|    "errors":[
+	|      "%s",
+	|      "%s"
+	|    ],
+	|    "exit_code":0,
+	|    "hierarchy":"trace2",
+	|    "name":"trace2",
+	|    "version":"$V"
+	|  }
+	|};
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index fd92533..ebd5fa5 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -10,7 +10,7 @@
 }
 
 # don't leave a stale daemon running
-trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+test_atexit 'git credential-cache exit'
 
 # test that the daemon works with no special setup
 helper_test cache
@@ -108,9 +108,4 @@
 
 helper_test_timeout cache --timeout=1
 
-# we can't rely on our "trap" above working after test_done,
-# as test_done will delete the trash directory containing
-# our socket, leaving us with no way to access the daemon.
-git credential-cache exit
-
 test_done
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index bce0278..5bd892f 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -518,6 +518,4 @@
 	git verify-pack --verbose "$IDX" | grep "$HASH"
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index a377530..7099d33 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -199,10 +199,6 @@
 	test_i18ngrep "too-short tree object" err
 '
 
-hex2oct() {
-    perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'malformed mode in tree' '
 	hex_sha1=$(echo foo | git hash-object --stdin -w) &&
 	bin_sha1=$(echo $hex_sha1 | hex2oct) &&
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index ae8a448..79f731d 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -232,25 +232,34 @@
 '
 
 test_expect_success 'gc.reflogexpire=never' '
+	test_config gc.reflogexpire never &&
+	test_config gc.reflogexpireunreachable never &&
 
-	git config gc.reflogexpire never &&
-	git config gc.reflogexpireunreachable never &&
-	git reflog expire --verbose --all &&
+	git reflog expire --verbose --all >output &&
+	test_line_count = 9 output &&
+
 	git reflog refs/heads/master >output &&
 	test_line_count = 4 output
 '
 
 test_expect_success 'gc.reflogexpire=false' '
+	test_config gc.reflogexpire false &&
+	test_config gc.reflogexpireunreachable false &&
 
-	git config gc.reflogexpire false &&
-	git config gc.reflogexpireunreachable false &&
 	git reflog expire --verbose --all &&
 	git reflog refs/heads/master >output &&
-	test_line_count = 4 output &&
+	test_line_count = 4 output
 
-	git config --unset gc.reflogexpire &&
-	git config --unset gc.reflogexpireunreachable
+'
 
+test_expect_success 'git reflog expire unknown reference' '
+	test_config gc.reflogexpire never &&
+	test_config gc.reflogexpireunreachable never &&
+
+	test_must_fail git reflog expire master@{123} 2>stderr &&
+	test_i18ngrep "points nowhere" stderr &&
+	test_must_fail git reflog expire does-not-exist 2>stderr &&
+	test_i18ngrep "points nowhere" stderr
 '
 
 test_expect_success 'checkout should not delete log for packed ref' '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 49f08d5..0f268a3 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -256,10 +256,6 @@
 	test_i18ngrep ! "fatal: empty filename in tree entry" out
 '
 
-hex2oct() {
-	perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'tree entry with type mismatch' '
 	test_when_finished "remove_object \$blob" &&
 	test_when_finished "remove_object \$tree" &&
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index afd4756..0aefada 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-test_description='git ls-files test (--others should pick up symlinks).
+test_description='basic tests for ls-files --others
 
 This test runs git ls-files --others with the following on the
 filesystem.
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
new file mode 100755
index 0000000..963f346
--- /dev/null
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='test git ls-files --others with non-submodule repositories
+
+This test runs git ls-files --others with the following working tree:
+
+    nonrepo-no-files/
+      plain directory with no files
+    nonrepo-untracked-file/
+      plain directory with an untracked file
+    repo-no-commit-no-files/
+      git repository without a commit or a file
+    repo-no-commit-untracked-file/
+      git repository without a commit but with an untracked file
+    repo-with-commit-no-files/
+      git repository with a commit and no untracked files
+    repo-with-commit-untracked-file/
+      git repository with a commit and an untracked file
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup: directories' '
+	mkdir nonrepo-no-files/ &&
+	mkdir nonrepo-untracked-file &&
+	: >nonrepo-untracked-file/untracked &&
+	git init repo-no-commit-no-files &&
+	git init repo-no-commit-untracked-file &&
+	: >repo-no-commit-untracked-file/untracked &&
+	git init repo-with-commit-no-files &&
+	git -C repo-with-commit-no-files commit --allow-empty -mmsg &&
+	git init repo-with-commit-untracked-file &&
+	test_commit -C repo-with-commit-untracked-file msg &&
+	: >repo-with-commit-untracked-file/untracked
+'
+
+test_expect_success 'ls-files --others handles untracked git repositories' '
+	git ls-files -o >output &&
+	cat >expect <<-EOF &&
+	nonrepo-untracked-file/untracked
+	output
+	repo-no-commit-no-files/
+	repo-no-commit-untracked-file/
+	repo-with-commit-no-files/
+	repo-with-commit-untracked-file/
+	EOF
+	test_cmp expect output
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 478b82c..e9ad50b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -264,6 +264,30 @@
 	test_must_fail git rev-parse refs/heads/t
 '
 
+test_expect_success 'deleting checked-out branch from repo that is a submodule' '
+	test_when_finished "rm -rf repo1 repo2" &&
+
+	git init repo1 &&
+	git init repo1/sub &&
+	test_commit -C repo1/sub x &&
+	git -C repo1 submodule add ./sub &&
+	git -C repo1 commit -m "adding sub" &&
+
+	git clone --recurse-submodules repo1 repo2 &&
+	git -C repo2/sub checkout -b work &&
+	test_must_fail git -C repo2/sub branch -D work
+'
+
+test_expect_success 'bare main worktree has HEAD at branch deleted by secondary worktree' '
+	test_when_finished "rm -rf nonbare base secondary" &&
+
+	git init nonbare &&
+	test_commit -C nonbare x &&
+	git clone --bare nonbare bare &&
+	git -C bare worktree add --detach ../secondary master &&
+	git -C secondary branch -D master
+'
+
 test_expect_success 'git branch --list -v with --abbrev' '
 	test_when_finished "git branch -D t" &&
 	git branch t &&
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 84bbf88..704bbc6 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -1120,9 +1120,10 @@
 	test_config notes.rewriteMode overwrite &&
 	test_config notes.rewriteRef refs/notes/other &&
 	echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
-	GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
+	GIT_NOTES_REWRITE_REF=refs/notes/commits \
+		git notes copy --for-rewrite=foo &&
 	git log -1 >actual &&
-	test_cmp expect actual
+	grep "replacement note 3" actual
 '
 
 test_expect_success 'git notes copy diagnoses too many or too few parameters' '
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index e0b5111..a0b9438 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -42,7 +42,7 @@
 		git checkout B^0 &&
 
 		set_fake_editor &&
-		FAKE_LINES="1" git rebase --interactive A &&
+		FAKE_LINES="1" git -c merge.directoryRenames=true rebase --interactive A &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -58,7 +58,7 @@
 
 		git checkout B^0 &&
 
-		git rebase A &&
+		git -c merge.directoryRenames=true rebase A &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -74,7 +74,7 @@
 
 		git checkout B^0 &&
 
-		git rebase --merge A &&
+		git -c merge.directoryRenames=true rebase --merge A &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -92,7 +92,7 @@
 
 		git format-patch -1 B &&
 
-		git am --3way 0001*.patch &&
+		git -c merge.directoryRenames=true am --3way 0001*.patch &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index 0db1661..1a8818f 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -25,6 +25,11 @@
 	test_commit base foo b &&
 	test_commit picked foo c &&
 	test_commit --signoff picked-signed foo d &&
+	git checkout -b topic initial &&
+	test_commit redundant-pick foo c redundant &&
+	git commit --allow-empty --allow-empty-message &&
+	git tag empty &&
+	git checkout master &&
 	git config advice.detachedhead false
 
 '
@@ -88,7 +93,7 @@
 
 test_expect_success 'cherry-pick w/dirty tree does not set CHERRY_PICK_HEAD' '
 	pristine_detach initial &&
-	echo foo > foo &&
+	echo foo >foo &&
 	test_must_fail git cherry-pick base &&
 	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
@@ -96,7 +101,7 @@
 test_expect_success \
 	'cherry-pick --strategy=resolve w/dirty tree does not set CHERRY_PICK_HEAD' '
 	pristine_detach initial &&
-	echo foo > foo &&
+	echo foo >foo &&
 	test_must_fail git cherry-pick --strategy=resolve base &&
 	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
@@ -175,23 +180,63 @@
 		git ls-files --stage foo &&
 		git checkout picked -- foo &&
 		git ls-files --stage foo
-	} > stages &&
+	} >stages &&
 	sed "
 		1 s/ 0	/ 1	/
 		2 s/ 0	/ 2	/
 		3 s/ 0	/ 3	/
-	" < stages > expected &&
+	" stages >expected &&
 	git read-tree -u --reset HEAD &&
 
 	test_must_fail git cherry-pick picked &&
-	git ls-files --stage --unmerged > actual &&
+	git ls-files --stage --unmerged >actual &&
 
 	test_cmp expected actual
 '
 
+test_expect_success \
+	'cherry-pick conflict, ensure commit.cleanup = scissors places scissors line properly' '
+	pristine_detach initial &&
+	git config commit.cleanup scissors &&
+	cat <<-EOF >expected &&
+		picked
+
+		# ------------------------ >8 ------------------------
+		# Do not modify or remove the line above.
+		# Everything below it will be ignored.
+		#
+		# Conflicts:
+		#	foo
+		EOF
+
+	test_must_fail git cherry-pick picked &&
+
+	test_i18ncmp expected .git/MERGE_MSG
+'
+
+test_expect_success \
+	'cherry-pick conflict, ensure cleanup=scissors places scissors line properly' '
+	pristine_detach initial &&
+	git config --unset commit.cleanup &&
+	cat <<-EOF >expected &&
+		picked
+
+		# ------------------------ >8 ------------------------
+		# Do not modify or remove the line above.
+		# Everything below it will be ignored.
+		#
+		# Conflicts:
+		#	foo
+		EOF
+
+	test_must_fail git cherry-pick --cleanup=scissors picked &&
+
+	test_i18ncmp expected .git/MERGE_MSG
+'
+
 test_expect_success 'failed cherry-pick describes conflict in work tree' '
 	pristine_detach initial &&
-	cat <<-EOF > expected &&
+	cat <<-EOF >expected &&
 	<<<<<<< HEAD
 	a
 	=======
@@ -201,14 +246,14 @@
 
 	test_must_fail git cherry-pick picked &&
 
-	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'diff3 -m style' '
 	pristine_detach initial &&
 	git config merge.conflictstyle diff3 &&
-	cat <<-EOF > expected &&
+	cat <<-EOF >expected &&
 	<<<<<<< HEAD
 	a
 	||||||| parent of objid picked
@@ -220,14 +265,14 @@
 
 	test_must_fail git cherry-pick picked &&
 
-	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'revert also handles conflicts sanely' '
 	git config --unset merge.conflictstyle &&
 	pristine_detach initial &&
-	cat <<-EOF > expected &&
+	cat <<-EOF >expected &&
 	<<<<<<< HEAD
 	a
 	=======
@@ -241,24 +286,24 @@
 		git ls-files --stage foo &&
 		git checkout base -- foo &&
 		git ls-files --stage foo
-	} > stages &&
+	} >stages &&
 	sed "
 		1 s/ 0	/ 1	/
 		2 s/ 0	/ 2	/
 		3 s/ 0	/ 3	/
-	" < stages > expected-stages &&
+	" stages >expected-stages &&
 	git read-tree -u --reset HEAD &&
 
 	head=$(git rev-parse HEAD) &&
 	test_must_fail git revert picked &&
 	newhead=$(git rev-parse HEAD) &&
-	git ls-files --stage --unmerged > actual-stages &&
+	git ls-files --stage --unmerged >actual-stages &&
 
 	test "$head" = "$newhead" &&
 	test_must_fail git update-index --refresh -q &&
 	test_must_fail git diff-index --exit-code HEAD &&
 	test_cmp expected-stages actual-stages &&
-	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
 	test_cmp expected actual
 '
 
@@ -284,7 +329,7 @@
 
 test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
 	pristine_detach base &&
-	echo foo > foo &&
+	echo foo >foo &&
 	test_must_fail git revert base &&
 	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
 	test_must_fail git rev-parse --verify REVERT_HEAD
@@ -319,7 +364,7 @@
 test_expect_success 'revert conflict, diff3 -m style' '
 	pristine_detach initial &&
 	git config merge.conflictstyle diff3 &&
-	cat <<-EOF > expected &&
+	cat <<-EOF >expected &&
 	<<<<<<< HEAD
 	a
 	||||||| objid picked
@@ -331,10 +376,56 @@
 
 	test_must_fail git revert picked &&
 
-	sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+	sed "s/[a-f0-9]*\.\.\./objid/" foo >actual &&
 	test_cmp expected actual
 '
 
+test_expect_success \
+	'revert conflict, ensure commit.cleanup = scissors places scissors line properly' '
+	pristine_detach initial &&
+	git config commit.cleanup scissors &&
+	cat >expected <<-EOF &&
+		Revert "picked"
+
+		This reverts commit OBJID.
+
+		# ------------------------ >8 ------------------------
+		# Do not modify or remove the line above.
+		# Everything below it will be ignored.
+		#
+		# Conflicts:
+		#	foo
+		EOF
+
+	test_must_fail git revert picked &&
+
+	sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
+	test_i18ncmp expected actual
+'
+
+test_expect_success \
+	'revert conflict, ensure cleanup=scissors places scissors line properly' '
+	pristine_detach initial &&
+	git config --unset commit.cleanup &&
+	cat >expected <<-EOF &&
+		Revert "picked"
+
+		This reverts commit OBJID.
+
+		# ------------------------ >8 ------------------------
+		# Do not modify or remove the line above.
+		# Everything below it will be ignored.
+		#
+		# Conflicts:
+		#	foo
+		EOF
+
+	test_must_fail git revert --cleanup=scissors picked &&
+
+	sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
+	test_i18ncmp expected actual
+'
+
 test_expect_success 'failed cherry-pick does not forget -s' '
 	pristine_detach initial &&
 	test_must_fail git cherry-pick -s picked &&
@@ -345,7 +436,7 @@
 	pristine_detach initial &&
 	test_must_fail git cherry-pick -s picked-signed &&
 	git commit -a -s &&
-	test $(git show -s |grep -c "Signed-off-by") = 1
+	test $(git show -s >tmp && grep -c "Signed-off-by" tmp && rm tmp) = 1
 '
 
 test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
@@ -359,7 +450,7 @@
 	Signed-off-by: C O Mitter <committer@example.com>
 	# Conflicts:
 	EOF
-	grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+	grep -e "^# Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
 	test_cmp expect actual &&
 
 	cat <<-\EOF >expected &&
@@ -378,7 +469,7 @@
 
 	# emulate old-style conflicts block
 	mv .git/MERGE_MSG .git/MERGE_MSG+ &&
-	sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG &&
+	sed -e "/^# Conflicts:/,\$s/^# *//" .git/MERGE_MSG+ >.git/MERGE_MSG &&
 
 	git commit -a &&
 	git commit --amend -s &&
@@ -388,7 +479,7 @@
 	Signed-off-by: C O Mitter <committer@example.com>
 	Conflicts:
 	EOF
-	grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+	grep -e "^Conflicts:" -e '^Signed-off-by' .git/COMMIT_EDITMSG >actual &&
 	test_cmp expect actual
 '
 
@@ -405,4 +496,23 @@
 	test_i18ngrep ! "Changes not staged for commit:" actual
 '
 
+test_expect_success 'cherry-pick --continue remembers --keep-redundant-commits' '
+	test_when_finished "git cherry-pick --abort || :" &&
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --keep-redundant-commits picked redundant &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue
+'
+
+test_expect_success 'cherry-pick --continue remembers --allow-empty and --allow-empty-message' '
+	test_when_finished "git cherry-pick --abort || :" &&
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --allow-empty --allow-empty-message \
+				       picked empty &&
+	echo c >foo &&
+	git add foo &&
+	git cherry-pick --continue
+'
+
 test_done
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
index 9888bf3..84a587d 100755
--- a/t/t3511-cherry-pick-x.sh
+++ b/t/t3511-cherry-pick-x.sh
@@ -298,4 +298,24 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x cleans commit message' '
+	pristine_detach initial &&
+	git cherry-pick -x mesg-unclean &&
+	git log -1 --pretty=format:%B >actual &&
+	printf "%s\n(cherry picked from commit %s)\n" \
+		"$mesg_unclean" $(git rev-parse mesg-unclean) |
+			git stripspace >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x respects commit.cleanup' '
+	pristine_detach initial &&
+	git -c commit.cleanup=strip cherry-pick -x mesg-unclean &&
+	git log -1 --pretty=format:%B >actual &&
+	printf "%s\n(cherry picked from commit %s)\n" \
+		"$mesg_unclean" $(git rev-parse mesg-unclean) |
+			git stripspace -s >expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index be582a5..c325167 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -296,6 +296,17 @@
 	)
 '
 
+test_expect_success 'error on a repository with no commits' '
+	rm -fr empty &&
+	git init empty &&
+	test_must_fail git add empty >actual 2>&1 &&
+	cat >expect <<-EOF &&
+	error: '"'empty/'"' does not have a commit checked out
+	fatal: adding files failed
+	EOF
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'git add --dry-run of existing changed file' "
 	echo new >>track-this &&
 	git add --dry-run track-this >actual 2>&1 &&
@@ -396,6 +407,7 @@
 '
 
 test_expect_success 'all statuses changed in folder if . is given' '
+	rm -fr empty &&
 	git add --chmod=+x . &&
 	test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
 	git add --chmod=-x . &&
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
index 4331b31..0168946 100755
--- a/t/t4053-diff-no-index.sh
+++ b/t/t4053-diff-no-index.sh
@@ -50,8 +50,7 @@
 		export GIT_CEILING_DIRECTORIES &&
 		cd non/git &&
 		test_must_fail git diff --no-index a 2>actual.err &&
-		echo "usage: git diff --no-index <path> <path>" >expect.err &&
-		test_cmp expect.err actual.err
+		test_i18ngrep "usage: git diff --no-index" actual.err
 	)
 '
 
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
new file mode 100755
index 0000000..90c8fb2
--- /dev/null
+++ b/t/t4067-diff-partial-clone.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='behavior of diff when reading objects in a partial clone'
+
+. ./test-lib.sh
+
+test_expect_success 'git show batches blobs' '
+	test_when_finished "rm -rf server client trace" &&
+
+	test_create_repo server &&
+	echo a >server/a &&
+	echo b >server/b &&
+	git -C server add a b &&
+	git -C server commit -m x &&
+
+	test_config -C server uploadpack.allowfilter 1 &&
+	test_config -C server uploadpack.allowanysha1inwant 1 &&
+	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+	# Ensure that there is exactly 1 negotiation by checking that there is
+	# only 1 "done" line sent. ("done" marks the end of negotiation.)
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client show HEAD &&
+	grep "git> done" trace >done_lines &&
+	test_line_count = 1 done_lines
+'
+
+test_expect_success 'diff batches blobs' '
+	test_when_finished "rm -rf server client trace" &&
+
+	test_create_repo server &&
+	echo a >server/a &&
+	echo b >server/b &&
+	git -C server add a b &&
+	git -C server commit -m x &&
+	echo c >server/c &&
+	echo d >server/d &&
+	git -C server add c d &&
+	git -C server commit -m x &&
+
+	test_config -C server uploadpack.allowfilter 1 &&
+	test_config -C server uploadpack.allowanysha1inwant 1 &&
+	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+	# Ensure that there is exactly 1 negotiation by checking that there is
+	# only 1 "done" line sent. ("done" marks the end of negotiation.)
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD &&
+	grep "git> done" trace >done_lines &&
+	test_line_count = 1 done_lines
+'
+
+test_expect_success 'diff skips same-OID blobs' '
+	test_when_finished "rm -rf server client trace" &&
+
+	test_create_repo server &&
+	echo a >server/a &&
+	echo b >server/b &&
+	git -C server add a b &&
+	git -C server commit -m x &&
+	echo another-a >server/a &&
+	git -C server add a &&
+	git -C server commit -m x &&
+
+	test_config -C server uploadpack.allowfilter 1 &&
+	test_config -C server uploadpack.allowanysha1inwant 1 &&
+	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+	echo a | git hash-object --stdin >hash-old-a &&
+	echo another-a | git hash-object --stdin >hash-new-a &&
+	echo b | git hash-object --stdin >hash-b &&
+
+	# Ensure that only a and another-a are fetched.
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD &&
+	grep "want $(cat hash-old-a)" trace &&
+	grep "want $(cat hash-new-a)" trace &&
+	! grep "want $(cat hash-b)" trace
+'
+
+test_expect_success 'diff with rename detection batches blobs' '
+	test_when_finished "rm -rf server client trace" &&
+
+	test_create_repo server &&
+	echo a >server/a &&
+	printf "b\nb\nb\nb\nb\n" >server/b &&
+	git -C server add a b &&
+	git -C server commit -m x &&
+	rm server/b &&
+	printf "b\nb\nb\nb\nbX\n" >server/c &&
+	git -C server add c &&
+	git -C server commit -a -m x &&
+
+	test_config -C server uploadpack.allowfilter 1 &&
+	test_config -C server uploadpack.allowanysha1inwant 1 &&
+	git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+	# Ensure that there is exactly 1 negotiation by checking that there is
+	# only 1 "done" line sent. ("done" marks the end of negotiation.)
+	GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff -M HEAD^ HEAD >out &&
+	grep "similarity index" out &&
+	grep "git> done" trace >done_lines &&
+	test_line_count = 1 done_lines
+'
+
+test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 1eeb828..df60f18 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -341,4 +341,12 @@
 	git prune --no-expire
 '
 
+test_expect_success 'trivial prune with bitmaps enabled' '
+	git repack -adb &&
+	blob=$(echo bitmap-unreachable-blob | git hash-object -w --stdin) &&
+	git prune --expire=now &&
+	git cat-file -e HEAD &&
+	test_must_fail git cat-file -e $blob
+'
+
 test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 82d7f7f..a26c8ba 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -34,8 +34,7 @@
 	bitmaptip=$(git rev-parse master) &&
 	blob=$(echo tagged-blob | git hash-object -w --stdin) &&
 	git tag tagged-blob $blob &&
-	git config repack.writebitmaps true &&
-	git config pack.writebitmaphashcache true
+	git config repack.writebitmaps true
 '
 
 test_expect_success 'full repack creates bitmaps' '
@@ -269,7 +268,7 @@
 	git clone --bare . compat-jgit.git &&
 	(
 		cd compat-jgit.git &&
-		rm -f .git/objects/pack/*.bitmap &&
+		rm -f objects/pack/*.bitmap &&
 		jgit gc &&
 		git rev-list --test-bitmap HEAD
 	)
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 069e4e2..840ad4d 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -366,6 +366,26 @@
 GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
 GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
 
+corrupt_graph_setup() {
+	cd "$TRASH_DIRECTORY/full" &&
+	test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
+	cp $objdir/info/commit-graph commit-graph-backup
+}
+
+corrupt_graph_verify() {
+	grepstr=$1
+	test_must_fail git commit-graph verify 2>test_err &&
+	grep -v "^+" test_err >err &&
+	test_i18ngrep "$grepstr" err &&
+	if test "$2" != "no-copy"
+	then
+		cp $objdir/info/commit-graph commit-graph-pre-write-test
+	fi &&
+	git status --short &&
+	GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD=true git commit-graph write &&
+	git commit-graph verify
+}
+
 # usage: corrupt_graph_and_verify <position> <data> <string> [<zero_pos>]
 # Manipulates the commit-graph file at the position
 # by inserting the data, optionally zeroing the file
@@ -376,19 +396,28 @@
 	pos=$1
 	data="${2:-\0}"
 	grepstr=$3
-	cd "$TRASH_DIRECTORY/full" &&
+	corrupt_graph_setup &&
 	orig_size=$(wc -c < $objdir/info/commit-graph) &&
 	zero_pos=${4:-${orig_size}} &&
-	test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
-	cp $objdir/info/commit-graph commit-graph-backup &&
 	printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
 	dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
 	generate_zero_bytes $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
-	test_must_fail git commit-graph verify 2>test_err &&
-	grep -v "^+" test_err >err &&
-	test_i18ngrep "$grepstr" err
+	corrupt_graph_verify "$grepstr"
+
 }
 
+test_expect_success POSIXPERM,SANITY 'detect permission problem' '
+	corrupt_graph_setup &&
+	chmod 000 $objdir/info/commit-graph &&
+	corrupt_graph_verify "Could not open" "no-copy"
+'
+
+test_expect_success 'detect too small' '
+	corrupt_graph_setup &&
+	echo "a small graph" >$objdir/info/commit-graph &&
+	corrupt_graph_verify "too small"
+'
+
 test_expect_success 'detect bad signature' '
 	corrupt_graph_and_verify 0 "\0" \
 		"graph signature"
@@ -499,6 +528,7 @@
 	git fsck &&
 	corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
 		"incorrect checksum" &&
+	cp commit-graph-pre-write-test $objdir/info/commit-graph &&
 	test_must_fail git fsck
 '
 
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 70926b5..1ebf19e 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -86,13 +86,14 @@
 '
 
 midx_git_two_modes () {
+	git -c core.multiPackIndex=false $1 >expect &&
+	git -c core.multiPackIndex=true $1 >actual &&
 	if [ "$2" = "sorted" ]
 	then
-		git -c core.multiPackIndex=false $1 | sort >expect &&
-		git -c core.multiPackIndex=true $1 | sort >actual
-	else
-		git -c core.multiPackIndex=false $1 >expect &&
-		git -c core.multiPackIndex=true $1 >actual
+		sort <expect >expect.sorted &&
+		mv expect.sorted expect &&
+		sort <actual >actual.sorted &&
+		mv actual.sorted actual
 	fi &&
 	test_cmp expect actual
 }
@@ -103,8 +104,8 @@
 		midx_git_two_modes "rev-list --objects --all" &&
 		midx_git_two_modes "log --raw" &&
 		midx_git_two_modes "count-objects --verbose" &&
-		midx_git_two_modes "cat-file --batch-all-objects --buffer --batch-check" &&
-		midx_git_two_modes "cat-file --batch-all-objects --buffer --batch-check --unsorted" sorted
+		midx_git_two_modes "cat-file --batch-all-objects --batch-check" &&
+		midx_git_two_modes "cat-file --batch-all-objects --batch-check --unordered" sorted
 	'
 }
 
@@ -116,6 +117,20 @@
 
 compare_results_with_midx "one v2 pack"
 
+test_expect_success 'corrupt idx not opened' '
+	idx=$(test-tool read-midx $objdir | grep "\.idx\$") &&
+	mv $objdir/pack/$idx backup-$idx &&
+	test_when_finished "mv backup-\$idx \$objdir/pack/\$idx" &&
+
+	# This is the minimum size for a sha-1 based .idx; this lets
+	# us pass perfunctory tests, but anything that actually opens and reads
+	# the idx file will complain.
+	test_copy_bytes 1064 <backup-$idx >$objdir/pack/$idx &&
+
+	git -c core.multiPackIndex=true rev-list --objects --all 2>err &&
+	test_must_be_empty err
+'
+
 test_expect_success 'add more objects' '
 	for i in $(test_seq 6 10)
 	do
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 0ef4d6f..1c71c0e 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -920,7 +920,4 @@
 	fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
 
-stop_httpd
-
-
 test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 3b7b305..e98d90d 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -978,6 +978,4 @@
 	check_negotiation_tip
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 49bf428..c81ca36 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1241,9 +1241,9 @@
 			cd shallow &&
 			# Some protocol versions (e.g. 2) support fetching
 			# unadvertised objects, so restrict this test to v0.
-			test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+			test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
 				git fetch ../testrepo/.git $SHA1_3 &&
-			test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+			test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
 				git fetch ../testrepo/.git $SHA1_1 &&
 			git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
 			git fetch ../testrepo/.git $SHA1_1 &&
@@ -1251,8 +1251,9 @@
 			test_must_fail git cat-file commit $SHA1_2 &&
 			git fetch ../testrepo/.git $SHA1_2 &&
 			git cat-file commit $SHA1_2 &&
-			test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
-				git fetch ../testrepo/.git $SHA1_3
+			test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+				git fetch ../testrepo/.git $SHA1_3 2>err &&
+			test_i18ngrep "remote error:.*not our ref.*$SHA1_3\$" err
 		)
 	'
 done
@@ -1284,6 +1285,17 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'peeled advertisements are not considered ref tips' '
+	mk_empty testrepo &&
+	git -C testrepo commit --allow-empty -m one &&
+	git -C testrepo commit --allow-empty -m two &&
+	git -C testrepo tag -m foo mytag HEAD^ &&
+	oid=$(git -C testrepo rev-parse mytag^{commit}) &&
+	test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+		git fetch testrepo $oid 2>err &&
+	test_i18ngrep "Server does not allow request for unadvertised object" err
+'
+
 test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
 	mk_test testrepo heads/master &&
 	rm -fr src dst &&
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index c19d8db..ccde8ba 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -77,6 +77,14 @@
 	test_must_be_empty out &&
 	test -s err)
 '
+test_expect_success 'git pull --cleanup errors early on invalid argument' '
+	mkdir clonedcleanup &&
+	(cd clonedcleanup && git init &&
+	test_must_fail git pull --cleanup invalid "../parent" >out 2>err &&
+	test_must_be_empty out &&
+	test -s err)
+'
+
 
 test_expect_success 'git pull --force' '
 	mkdir clonedoldstyle &&
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 4f6e32b..a1d3031 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -57,13 +57,25 @@
 	grep "bad tree object" output.err
 '
 
-test_expect_success 'upload-pack error message when bad ref requested' '
+test_expect_success 'upload-pack fails due to bad want (no object)' '
 
 	printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
 		"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
 	test_must_fail git upload-pack . <input >output 2>output.err &&
-	grep -q "not our ref" output.err &&
-	! grep -q multi_ack_detailed output.err
+	grep "not our ref" output.err &&
+	grep "ERR" output &&
+	! grep multi_ack_detailed output.err
+'
+
+test_expect_success 'upload-pack fails due to bad want (not tip)' '
+
+	oid=$(echo an object we have | git hash-object -w --stdin) &&
+	printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+		"$oid" >input &&
+	test_must_fail git upload-pack . <input >output 2>output.err &&
+	grep "not our ref" output.err &&
+	grep "ERR" output &&
+	! grep multi_ack_detailed output.err
 '
 
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 6caf628..66f0b64 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -255,6 +255,4 @@
 	git -C client fsck
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
index cdb687b..b4ad81f 100755
--- a/t/t5539-fetch-http-shallow.sh
+++ b/t/t5539-fetch-http-shallow.sh
@@ -149,5 +149,4 @@
 	)
 '
 
-stop_httpd
 test_done
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
index 88ff5a4..a094fd5 100755
--- a/t/t5540-http-push-webdav.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -176,6 +176,4 @@
 	test_cmp expect actual
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 0e3055a..8ef8763 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -383,5 +383,4 @@
 	test_i18ngrep ! "^hint: " decoded
 '
 
-stop_httpd
 test_done
diff --git a/t/t5542-push-http-shallow.sh b/t/t5542-push-http-shallow.sh
index 5165833..ddc1db7 100755
--- a/t/t5542-push-http-shallow.sh
+++ b/t/t5542-push-http-shallow.sh
@@ -90,5 +90,4 @@
 	)
 '
 
-stop_httpd
 test_done
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index b47a958..6d1d59c 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -278,6 +278,4 @@
 	test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 694b77c..b811d89 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -424,5 +424,4 @@
 	git -c http.followredirects=true clone "$HTTPD_URL/dumb/alt-child.git"
 '
 
-stop_httpd
 test_done
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index c760514..ac74626 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -469,5 +469,4 @@
 	grep "server-side error" actual
 '
 
-stop_httpd
 test_done
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index 1c49054..6eb0294 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -132,5 +132,4 @@
 	check_access_log exp
 '
 
-stop_httpd
 test_done
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index 58ee787..34487bb 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -90,6 +90,7 @@
 test_expect_success 'fetch notices corrupt idx' '
 	cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
 	(cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+	 rm -f objects/pack/multi-pack-index &&
 	 p=$(ls objects/pack/pack-*.idx) &&
 	 chmod u+w $p &&
 	 printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
@@ -198,5 +199,4 @@
 	test_cmp expect actual
 '
 
-stop_git_daemon
 test_done
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh
index cd9283e..5129b07 100755
--- a/t/t5581-http-curl-verbose.sh
+++ b/t/t5581-http-curl-verbose.sh
@@ -23,6 +23,4 @@
 	grep "< HTTP/1.1 500 Intentional Breakage" curl_log
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index a454b14..de9d99c 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -611,10 +611,6 @@
 	git -C replay.git index-pack -v --stdin <tmp.pack
 '
 
-hex2oct () {
-	perl -ne 'printf "\\%03o", hex for /../g'
-}
-
 test_expect_success 'clone on case-insensitive fs' '
 	git init icasefs &&
 	(
@@ -733,6 +729,4 @@
 	partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 9643acb..9a8f988 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -331,6 +331,4 @@
 	! test -e "$HTTPD_ROOT_PATH/one-time-sed"
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index d5ed196..7c9511c 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -292,6 +292,4 @@
 	grep "git< version 1" log
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index fe45bf8..ffb9613 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='test git-serve and server commands'
+test_description='test protocol v2 server commands'
 
 . ./test-lib.sh
 
@@ -14,7 +14,8 @@
 	0000
 	EOF
 
-	GIT_TEST_SIDEBAND_ALL=0 git serve --advertise-capabilities >out &&
+	GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
+		--advertise-capabilities >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -24,11 +25,11 @@
 	test-tool pkt-line pack >in <<-EOF &&
 	0000
 	EOF
-	git serve --stateless-rpc >out <in &&
+	test-tool serve-v2 --stateless-rpc >out <in &&
 	test_must_be_empty out &&
 
 	# EOF
-	git serve --stateless-rpc >out &&
+	test-tool serve-v2 --stateless-rpc >out &&
 	test_must_be_empty out
 '
 
@@ -37,7 +38,7 @@
 	foobar
 	0000
 	EOF
-	test_must_fail git serve --stateless-rpc 2>err <in &&
+	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
 	test_i18ngrep "unknown capability" err
 '
 
@@ -46,7 +47,7 @@
 	agent=git/test
 	0000
 	EOF
-	test_must_fail git serve --stateless-rpc 2>err <in &&
+	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
 	test_i18ngrep "no command requested" err
 '
 
@@ -56,7 +57,7 @@
 	agent=git/test
 	0000
 	EOF
-	test_must_fail git serve --stateless-rpc 2>err <in &&
+	test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
 	test_i18ngrep "invalid command" err
 '
 
@@ -87,7 +88,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -107,7 +108,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -127,7 +128,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -148,7 +149,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -169,7 +170,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -189,7 +190,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc <in >out &&
+	test-tool serve-v2 --stateless-rpc <in >out &&
 	test-tool pkt-line unpack <out >actual &&
 	test_cmp expect actual
 '
@@ -204,7 +205,10 @@
 	0000
 	EOF
 
-	test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
+	(
+		cd server &&
+		test_must_fail test-tool serve-v2 --stateless-rpc
+	) <in >/dev/null 2>err &&
 	grep "unexpected line: .this-is-not-a-command." err
 '
 
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index e112b60..5b33f62 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -182,6 +182,13 @@
 	grep "server-option=world" log
 '
 
+test_expect_success 'warn if using server-option with ls-remote with legacy protocol' '
+	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
+		ls-remote -o hello -o world "file://$(pwd)/file_parent" master 2>err &&
+
+	test_i18ngrep "see protocol.version in" err &&
+	test_i18ngrep "server options require protocol version 2 or later" err
+'
 
 test_expect_success 'clone with file:// using protocol v2' '
 	test_when_finished "rm -f log" &&
@@ -251,6 +258,40 @@
 	grep "server-option=world" log
 '
 
+test_expect_success 'warn if using server-option with fetch with legacy protocol' '
+	test_when_finished "rm -rf temp_child" &&
+
+	git init temp_child &&
+
+	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C temp_child -c protocol.version=0 \
+		fetch -o hello -o world "file://$(pwd)/file_parent" master 2>err &&
+
+	test_i18ngrep "see protocol.version in" err &&
+	test_i18ngrep "server options require protocol version 2 or later" err
+'
+
+test_expect_success 'server-options are sent when cloning' '
+	test_when_finished "rm -rf log myclone" &&
+
+	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
+		clone --server-option=hello --server-option=world \
+		"file://$(pwd)/file_parent" myclone &&
+
+	grep "server-option=hello" log &&
+	grep "server-option=world" log
+'
+
+test_expect_success 'warn if using server-option with clone with legacy protocol' '
+	test_when_finished "rm -rf myclone" &&
+
+	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -c protocol.version=0 \
+		clone --server-option=hello --server-option=world \
+		"file://$(pwd)/file_parent" myclone 2>err &&
+
+	test_i18ngrep "see protocol.version in" err &&
+	test_i18ngrep "server options require protocol version 2 or later" err
+'
+
 test_expect_success 'upload-pack respects config using protocol v2' '
 	git init server &&
 	write_script server/.git/hook <<-\EOF &&
@@ -359,12 +400,13 @@
 	0000
 	EOF
 
-	test_must_fail git -C server serve --stateless-rpc <in >/dev/null 2>err &&
+	test_must_fail test-tool -C server serve-v2 --stateless-rpc \
+		<in >/dev/null 2>err &&
 	grep "unexpected line: .filter blob:none." err &&
 
 	# Exercise to ensure that if advertised, filter works
 	git -C server config uploadpack.allowfilter 1 &&
-	git -C server serve --stateless-rpc <in >/dev/null
+	test-tool -C server serve-v2 --stateless-rpc <in >/dev/null
 '
 
 test_expect_success 'default refspec is used to filter ref when fetchcing' '
@@ -687,6 +729,4 @@
 	test_i18ngrep "expected no other sections to be sent after no .ready." err
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index f87b2f6..0951d1b 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -48,15 +48,15 @@
 '
 
 test_expect_success 'config controls ref-in-want advertisement' '
-	git serve --advertise-capabilities >out &&
+	test-tool serve-v2 --advertise-capabilities >out &&
 	! grep -a ref-in-want out &&
 
 	git config uploadpack.allowRefInWant false &&
-	git serve --advertise-capabilities >out &&
+	test-tool serve-v2 --advertise-capabilities >out &&
 	! grep -a ref-in-want out &&
 
 	git config uploadpack.allowRefInWant true &&
-	git serve --advertise-capabilities >out &&
+	test-tool serve-v2 --advertise-capabilities >out &&
 	grep -a ref-in-want out
 '
 
@@ -70,7 +70,7 @@
 	0000
 	EOF
 
-	test_must_fail git serve --stateless-rpc 2>out <in &&
+	test_must_fail test-tool serve-v2 --stateless-rpc 2>out <in &&
 	grep "unknown ref" out
 '
 
@@ -90,7 +90,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc >out <in &&
+	test-tool serve-v2 --stateless-rpc >out <in &&
 	check_output
 '
 
@@ -112,7 +112,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc >out <in &&
+	test-tool serve-v2 --stateless-rpc >out <in &&
 	check_output
 '
 
@@ -133,7 +133,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc >out <in &&
+	test-tool serve-v2 --stateless-rpc >out <in &&
 	check_output
 '
 
@@ -153,7 +153,7 @@
 	0000
 	EOF
 
-	git serve --stateless-rpc >out <in &&
+	test-tool serve-v2 --stateless-rpc >out <in &&
 	check_output
 '
 
@@ -257,8 +257,6 @@
 	test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
 '
 
-stop_httpd
-
 REPO="$(pwd)/repo"
 LOCAL_PRISTINE="$(pwd)/local_pristine"
 
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index aaaa722..d04f800 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -8,6 +8,8 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
+PATH="$TEST_DIRECTORY/t5801:$PATH"
+
 compare_refs() {
 	git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
 	git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
diff --git a/git-remote-testgit.sh b/t/t5801/git-remote-testgit
similarity index 100%
rename from git-remote-testgit.sh
rename to t/t5801/git-remote-testgit
diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh
index 872788a..af8772f 100755
--- a/t/t5812-proto-disable-http.sh
+++ b/t/t5812-proto-disable-http.sh
@@ -34,5 +34,4 @@
 		clone "$HTTPD_URL/smart-redir-perm/repo.git" redir.git
 '
 
-stop_httpd
 test_done
diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh
index 62c5647..50b7543 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -75,7 +75,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -142,7 +142,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -201,7 +201,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -270,7 +270,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 
 		git ls-files -s >out &&
@@ -350,7 +350,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -416,7 +416,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -497,7 +497,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT.*directory rename split" out &&
 
 		git ls-files -s >out &&
@@ -559,7 +559,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 >out &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -640,7 +640,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -705,7 +705,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep CONFLICT.*rename/rename.*z/d.*x/d.*w/d out &&
 		test_i18ngrep ! CONFLICT.*rename/rename.*y/d out &&
 
@@ -826,7 +826,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 5 out &&
@@ -915,7 +915,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT.*implicit dir rename" out &&
 
 		git ls-files -s >out &&
@@ -989,7 +989,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (add/add).* y/d" out &&
 
 		git ls-files -s >out &&
@@ -1069,7 +1069,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*z/d" out &&
 		test_i18ngrep "CONFLICT (add/add).* y/d" out &&
 
@@ -1153,7 +1153,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (file/directory).*y/d" out &&
 
 		git ls-files -s >out &&
@@ -1243,7 +1243,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/delete).*z/c.*y/c" out &&
 
 		git ls-files -s >out &&
@@ -1308,7 +1308,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -1370,7 +1370,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -1432,7 +1432,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -1495,7 +1495,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -1591,7 +1591,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/rename).*z/b.*y/b.*w/b" out &&
 		test_i18ngrep "CONFLICT (rename/rename).*z/c.*y/c.*x/c" out &&
 
@@ -1663,7 +1663,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 
 		git ls-files -s >out &&
@@ -1740,7 +1740,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/rename).*x/d.*w/d.*y/d" out &&
 
 		git ls-files -s >out &&
@@ -1804,7 +1804,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
 		git ls-files -s >out &&
@@ -1894,7 +1894,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (rename/delete).*x/d.*y/d" out &&
 
 		git ls-files -s >out &&
@@ -1985,7 +1985,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -2063,7 +2063,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -2135,7 +2135,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "CONFLICT (modify/delete).* z/d" out &&
 
 		git ls-files -s >out &&
@@ -2212,7 +2212,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -2287,7 +2287,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep CONFLICT.*rename/rename.*z/c.*y/c.*w/c out &&
 		test_i18ngrep CONFLICT.*rename/rename.*z/b.*y/b.*w/b out &&
 
@@ -2374,7 +2374,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 7 out &&
@@ -2444,7 +2444,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -2534,7 +2534,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 >out &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "WARNING: Avoiding applying x -> z rename to x/f" out &&
 
 		git ls-files -s >out &&
@@ -2622,7 +2622,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 >out &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		test_i18ngrep "WARNING: Avoiding applying z -> y rename to z/t" out &&
 		test_i18ngrep "WARNING: Avoiding applying y -> x rename to y/a" out &&
 		test_i18ngrep "WARNING: Avoiding applying x -> w rename to x/b" out &&
@@ -2704,7 +2704,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 >out &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
 		grep "CONFLICT (implicit dir rename): Cannot map more than one path to combined/yo" out >error_line &&
 		grep -q dir1/yo error_line &&
 		grep -q dir2/yo error_line &&
@@ -2782,7 +2782,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -2849,7 +2849,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -2918,7 +2918,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 3 out &&
@@ -2993,7 +2993,7 @@
 		echo very >z/c &&
 		echo important >z/d &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "The following untracked working tree files would be overwritten by merge" err &&
 
 		git ls-files -s >out &&
@@ -3061,7 +3061,7 @@
 		echo important >y/d &&
 		echo contents >y/e &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
 		test_i18ngrep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
 
@@ -3137,7 +3137,7 @@
 		git checkout A^0 &&
 		echo important >y/c &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 		test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
 
@@ -3174,7 +3174,7 @@
 		mkdir y &&
 		echo important >y/c &&
 
-		test_must_fail git merge -s recursive A^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 		test_i18ngrep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
 
@@ -3249,7 +3249,7 @@
 		git checkout A^0 &&
 		echo important >y/wham &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 		test_i18ngrep "Refusing to lose untracked file at y/wham" out &&
 
@@ -3327,7 +3327,7 @@
 		mkdir z &&
 		echo random >z/c &&
 
-		git merge -s recursive B^0 >out 2>err &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep ! "following untracked working tree files would be overwritten by merge" err &&
 
 		git ls-files -s >out &&
@@ -3407,7 +3407,7 @@
 		git checkout A^0 &&
 		echo stuff >>z/c &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
 		test_seq 1 10 >expected &&
@@ -3479,7 +3479,7 @@
 		git checkout A^0 &&
 		echo stuff >>z/c &&
 
-		git merge -s recursive B^0 >out 2>err &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
 		grep -q stuff z/c &&
@@ -3554,7 +3554,7 @@
 		git checkout A^0 &&
 		echo stuff >>y/c &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "following files would be overwritten by merge" err &&
 
 		grep -q stuff y/c &&
@@ -3621,7 +3621,7 @@
 		git checkout A^0 &&
 		echo stuff >>z/c &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "Refusing to lose dirty file at z/c" out &&
 
 		grep -q stuff z/c &&
@@ -3700,7 +3700,7 @@
 		git checkout A^0 &&
 		echo mods >>y/c &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 		test_i18ngrep "Refusing to lose dirty file at y/c" out &&
 
@@ -3782,7 +3782,7 @@
 		git checkout A^0 &&
 		echo important >>y/wham &&
 
-		test_must_fail git merge -s recursive B^0 >out 2>err &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 		test_i18ngrep "CONFLICT (rename/rename)" out &&
 		test_i18ngrep "Refusing to lose dirty file at y/wham" out &&
 
@@ -3870,7 +3870,7 @@
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 6 out &&
@@ -3910,7 +3910,7 @@
 #         To which, I can do no more than shrug my shoulders and say that
 #         even simple rules give weird results when given weird inputs.
 
-test_expect_success '12b-setup: Moving one directory hierarchy into another' '
+test_expect_success '12b-setup: Moving two directory hierarchies into each other' '
 	test_create_repo 12b &&
 	(
 		cd 12b &&
@@ -3940,13 +3940,13 @@
 	)
 '
 
-test_expect_success '12b-check: Moving one directory hierarchy into another' '
+test_expect_success '12b-check: Moving two directory hierarchies into each other' '
 	(
 		cd 12b &&
 
 		git checkout A^0 &&
 
-		git merge -s recursive B^0 &&
+		git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -s >out &&
 		test_line_count = 4 out &&
@@ -4016,7 +4016,7 @@
 
 		git checkout A^0 &&
 
-		test_must_fail git merge -s recursive B^0 &&
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
 		git ls-files -u >out &&
 		test_line_count = 12 out &&
@@ -4051,4 +4051,356 @@
 	)
 '
 
+###########################################################################
+# SECTION 13: Checking informational and conflict messages
+#
+# A year after directory rename detection became the default, it was
+# instead decided to report conflicts on the pathname on the basis that
+# some users may expect the new files added or moved into a directory to
+# be unrelated to all the other files in that directory, and thus that
+# directory rename detection is unexpected.  Test that the messages printed
+# match our expectation.
+###########################################################################
+
+# Testcase 13a, Basic directory rename with newly added files
+#   Commit O: z/{b,c}
+#   Commit A: y/{b,c}
+#   Commit B: z/{b,c,d,e/f}
+#   Expected: y/{b,c,d,e/f}, with notices/conflicts for both y/d and y/e/f
+
+test_expect_success '13a-setup: messages for newly added files' '
+	test_create_repo 13a &&
+	(
+		cd 13a &&
+
+		mkdir z &&
+		echo b >z/b &&
+		echo c >z/c &&
+		git add z &&
+		test_tick &&
+		git commit -m "O" &&
+
+		git branch O &&
+		git branch A &&
+		git branch B &&
+
+		git checkout A &&
+		git mv z y &&
+		test_tick &&
+		git commit -m "A" &&
+
+		git checkout B &&
+		echo d >z/d &&
+		mkdir z/e &&
+		echo f >z/e/f &&
+		git add z/d z/e/f &&
+		test_tick &&
+		git commit -m "B"
+	)
+'
+
+test_expect_success '13a-check(conflict): messages for newly added files' '
+	(
+		cd 13a &&
+
+		git checkout A^0 &&
+
+		test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep CONFLICT..file.location.*z/e/f.added.in.B^0.*y/e/f out &&
+		test_i18ngrep CONFLICT..file.location.*z/d.added.in.B^0.*y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/[de]" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d &&
+		test_path_is_missing z/e/f &&
+		test_path_is_file    y/e/f
+	)
+'
+
+test_expect_success '13a-check(info): messages for newly added files' '
+	(
+		cd 13a &&
+
+		git reset --hard &&
+		git checkout A^0 &&
+
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep Path.updated:.*z/e/f.added.in.B^0.*y/e/f out &&
+		test_i18ngrep Path.updated:.*z/d.added.in.B^0.*y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/[de]" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d &&
+		test_path_is_missing z/e/f &&
+		test_path_is_file    y/e/f
+	)
+'
+
+# Testcase 13b, Transitive rename with conflicted content merge and default
+#               "conflict" setting
+#   (Related to testcase 1c, 9b)
+#   Commit O: z/{b,c},   x/d_1
+#   Commit A: y/{b,c},   x/d_2
+#   Commit B: z/{b,c,d_3}
+#   Expected: y/{b,c,d_merged}, with two conflict messages for y/d,
+#             one about content, and one about file location
+
+test_expect_success '13b-setup: messages for transitive rename with conflicted content' '
+	test_create_repo 13b &&
+	(
+		cd 13b &&
+
+		mkdir x &&
+		mkdir z &&
+		test_seq 1 10 >x/d &&
+		echo b >z/b &&
+		echo c >z/c &&
+		git add x z &&
+		test_tick &&
+		git commit -m "O" &&
+
+		git branch O &&
+		git branch A &&
+		git branch B &&
+
+		git checkout A &&
+		git mv z y &&
+		echo 11 >>x/d &&
+		git add x/d &&
+		test_tick &&
+		git commit -m "A" &&
+
+		git checkout B &&
+		echo eleven >>x/d &&
+		git mv x/d z/d &&
+		git add z/d &&
+		test_tick &&
+		git commit -m "B"
+	)
+'
+
+test_expect_success '13b-check(conflict): messages for transitive rename with conflicted content' '
+	(
+		cd 13b &&
+
+		git checkout A^0 &&
+
+		test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+		test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/d" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d
+	)
+'
+
+test_expect_success '13b-check(info): messages for transitive rename with conflicted content' '
+	(
+		cd 13b &&
+
+		git reset --hard &&
+		git checkout A^0 &&
+
+		test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep CONFLICT.*content.*Merge.conflict.in.y/d out &&
+		test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/d" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d
+	)
+'
+
+# Testcase 13c, Rename/rename(1to1) due to directory rename
+#   Commit O: z/{b,c},   x/{d,e}
+#   Commit A: y/{b,c,d}, x/e
+#   Commit B: z/{b,c,d}, x/e
+#   Expected: y/{b,c,d}, with info or conflict messages for d (
+#             A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
+#             One could argue A had partial knowledge of what was done with
+#             d and B had full knowledge, but that's a slippery slope as
+#             shown in testcase 13d.
+
+test_expect_success '13c-setup: messages for rename/rename(1to1) via transitive rename' '
+	test_create_repo 13c &&
+	(
+		cd 13c &&
+
+		mkdir x &&
+		mkdir z &&
+		test_seq 1 10 >x/d &&
+		echo e >x/e &&
+		echo b >z/b &&
+		echo c >z/c &&
+		git add x z &&
+		test_tick &&
+		git commit -m "O" &&
+
+		git branch O &&
+		git branch A &&
+		git branch B &&
+
+		git checkout A &&
+		git mv z y &&
+		git mv x/d y/ &&
+		test_tick &&
+		git commit -m "A" &&
+
+		git checkout B &&
+		git mv x/d z/d &&
+		git add z/d &&
+		test_tick &&
+		git commit -m "B"
+	)
+'
+
+test_expect_success '13c-check(conflict): messages for rename/rename(1to1) via transitive rename' '
+	(
+		cd 13c &&
+
+		git checkout A^0 &&
+
+		test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep CONFLICT..file.location.*x/d.renamed.to.z/d.*moved.to.y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/d" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d
+	)
+'
+
+test_expect_success '13c-check(info): messages for rename/rename(1to1) via transitive rename' '
+	(
+		cd 13c &&
+
+		git reset --hard &&
+		git checkout A^0 &&
+
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep Path.updated:.*x/d.renamed.to.z/d.in.B^0.*moving.it.to.y/d out &&
+
+		git ls-files >paths &&
+		! grep z/ paths &&
+		grep "y/d" paths &&
+
+		test_path_is_missing z/d &&
+		test_path_is_file    y/d
+	)
+'
+
+# Testcase 13d, Rename/rename(1to1) due to directory rename on both sides
+#   Commit O: a/{z,y}, b/x,     c/w
+#   Commit A: a/z,     b/{y,x}, d/w
+#   Commit B: a/z,     d/x,     c/{y,w}
+#   Expected: a/z, d/{y,x,w} with no file location conflict for x
+#             Easy cases:
+#               * z is always in a; so it stays in a.
+#               * x starts in b, only modified on one side to move into d/
+#               * w starts in c, only modified on one side to move into d/
+#             Hard case:
+#               * A renames a/y to b/y, and B renames b/->d/ => a/y -> d/y
+#               * B renames a/y to c/y, and A renames c/->d/ => a/y -> d/y
+#               No conflict in where a/y ends up, so put it in d/y.
+
+test_expect_success '13d-setup: messages for rename/rename(1to1) via dual transitive rename' '
+	test_create_repo 13d &&
+	(
+		cd 13d &&
+
+		mkdir a &&
+		mkdir b &&
+		mkdir c &&
+		echo z >a/z &&
+		echo y >a/y &&
+		echo x >b/x &&
+		echo w >c/w &&
+		git add a b c &&
+		test_tick &&
+		git commit -m "O" &&
+
+		git branch O &&
+		git branch A &&
+		git branch B &&
+
+		git checkout A &&
+		git mv a/y b/ &&
+		git mv c/ d/ &&
+		test_tick &&
+		git commit -m "A" &&
+
+		git checkout B &&
+		git mv a/y c/ &&
+		git mv b/ d/ &&
+		test_tick &&
+		git commit -m "B"
+	)
+'
+
+test_expect_success '13d-check(conflict): messages for rename/rename(1to1) via dual transitive rename' '
+	(
+		cd 13d &&
+
+		git checkout A^0 &&
+
+		test_must_fail git merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.b/y.*moved.to.d/y out &&
+		test_i18ngrep CONFLICT..file.location.*a/y.renamed.to.c/y.*moved.to.d/y out &&
+
+		git ls-files >paths &&
+		! grep b/ paths &&
+		! grep c/ paths &&
+		grep "d/y" paths &&
+
+		test_path_is_missing b/y &&
+		test_path_is_missing c/y &&
+		test_path_is_file    d/y
+	)
+'
+
+test_expect_success '13d-check(info): messages for rename/rename(1to1) via dual transitive rename' '
+	(
+		cd 13d &&
+
+		git reset --hard &&
+		git checkout A^0 &&
+
+		git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+
+		test_i18ngrep Path.updated.*a/y.renamed.to.b/y.*moving.it.to.d/y out &&
+		test_i18ngrep Path.updated.*a/y.renamed.to.c/y.*moving.it.to.d/y out &&
+
+		git ls-files >paths &&
+		! grep b/ paths &&
+		! grep c/ paths &&
+		grep "d/y" paths &&
+
+		test_path_is_missing b/y &&
+		test_path_is_missing c/y &&
+		test_path_is_file    d/y
+	)
+'
+
 test_done
diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6046-merge-skip-unneeded-updates.sh
index 38e24f7..3a47623 100755
--- a/t/t6046-merge-skip-unneeded-updates.sh
+++ b/t/t6046-merge-skip-unneeded-updates.sh
@@ -466,7 +466,7 @@
 
 		git checkout A^0 &&
 
-		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
+		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
 		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
@@ -495,7 +495,7 @@
 
 		git checkout B^0 &&
 
-		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
+		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
 		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
@@ -560,7 +560,7 @@
 
 		git checkout A^0 &&
 
-		GIT_MERGE_VERBOSITY=3 git merge -s recursive B^0 >out 2>err &&
+		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
 
 		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
@@ -589,7 +589,7 @@
 
 		git checkout B^0 &&
 
-		GIT_MERGE_VERBOSITY=3 git merge -s recursive A^0 >out 2>err &&
+		GIT_MERGE_VERBOSITY=3 git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
 
 		test_i18ngrep ! "Skipped bar/bq" out &&
 		test_must_be_empty err &&
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index d638119..e7e64e0 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -40,7 +40,8 @@
 	test "$_found" = "$_parent" || return 1
 	_parent_number=$(( $_parent_number + 1 ))
     done &&
-    test_must_fail git rev-parse --verify $_commit^$_parent_number
+    test_must_fail git rev-parse --verify $_commit^$_parent_number 2>err &&
+    test_i18ngrep "Needed a single revision" err
 }
 
 commit_has_parents ()
@@ -393,9 +394,11 @@
 '
 
 test_expect_success '--graft with and without already replaced object' '
-	test $(git log --oneline | wc -l) = 7 &&
+	git log --oneline >log &&
+	test_line_count = 7 log &&
 	git replace --graft $HASH5 &&
-	test $(git log --oneline | wc -l) = 3 &&
+	git log --oneline >log &&
+	test_line_count = 3 log &&
 	commit_has_parents $HASH5 &&
 	test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
 	git replace --force -g $HASH5 $HASH4 $HASH3 &&
@@ -403,6 +406,28 @@
 	git replace -d $HASH5
 '
 
+test_expect_success '--graft using a tag as the new parent' '
+	git tag new_parent $HASH5 &&
+	git replace --graft $HASH7 new_parent &&
+	commit_has_parents $HASH7 $HASH5 &&
+	git replace -d $HASH7 &&
+	git tag -a -m "annotated new parent tag" annotated_new_parent $HASH5 &&
+	git replace --graft $HASH7 annotated_new_parent &&
+	commit_has_parents $HASH7 $HASH5 &&
+	git replace -d $HASH7
+'
+
+test_expect_success '--graft using a tag as the replaced object' '
+	git tag replaced_object $HASH7 &&
+	git replace --graft replaced_object $HASH5 &&
+	commit_has_parents $HASH7 $HASH5 &&
+	git replace -d $HASH7 &&
+	git tag -a -m "annotated replaced object tag" annotated_replaced_object $HASH7 &&
+	git replace --graft annotated_replaced_object $HASH5 &&
+	commit_has_parents $HASH7 $HASH5 &&
+	git replace -d $HASH7
+'
+
 test_expect_success GPG 'set up a signed commit' '
 	echo "line 17" >>hello &&
 	echo "line 18" >>hello &&
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
new file mode 100755
index 0000000..28611c9
--- /dev/null
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='git rev-list should handle unexpected object types'
+
+. ./test-lib.sh
+
+test_expect_success 'setup well-formed objects' '
+	blob="$(printf "foo" | git hash-object -w --stdin)" &&
+	tree="$(printf "100644 blob $blob\tfoo" | git mktree)" &&
+	commit="$(git commit-tree $tree -m "first commit")" &&
+	git cat-file commit $commit >good-commit
+'
+
+test_expect_success 'setup unexpected non-blob entry' '
+	printf "100644 foo\0$(echo $tree | hex2oct)" >broken-tree &&
+	broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_failure 'traverse unexpected non-blob entry (lone)' '
+	test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-blob entry (seen)' '
+	test_must_fail git rev-list --objects $tree $broken_tree >output 2>&1 &&
+	test_i18ngrep "is not a blob" output
+'
+
+test_expect_success 'setup unexpected non-tree entry' '
+	printf "40000 foo\0$(echo $blob | hex2oct)" >broken-tree &&
+	broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
+'
+
+test_expect_success 'traverse unexpected non-tree entry (lone)' '
+	test_must_fail git rev-list --objects $broken_tree
+'
+
+test_expect_success 'traverse unexpected non-tree entry (seen)' '
+	test_must_fail git rev-list --objects $blob $broken_tree >output 2>&1 &&
+	test_i18ngrep "is not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit parent' '
+	sed "/^author/ { h; s/.*/parent $blob/; G; }" <good-commit \
+		>broken-commit &&
+	broken_commit="$(git hash-object -w --literally -t commit \
+		broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-commit parent (lone)' '
+	test_must_fail git rev-list --objects $broken_commit >output 2>&1 &&
+	test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'traverse unexpected non-commit parent (seen)' '
+	test_must_fail git rev-list --objects $commit $broken_commit \
+		>output 2>&1 &&
+	test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree root' '
+	sed -e "s/$tree/$blob/" <good-commit >broken-commit &&
+	broken_commit="$(git hash-object -w --literally -t commit \
+		broken-commit)"
+'
+
+test_expect_success 'traverse unexpected non-tree root (lone)' '
+	test_must_fail git rev-list --objects $broken_commit
+'
+
+test_expect_success 'traverse unexpected non-tree root (seen)' '
+	test_must_fail git rev-list --objects $blob $broken_commit \
+		>output 2>&1 &&
+	test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-commit tag' '
+	git tag -a -m "tagged commit" tag $commit &&
+	git cat-file tag tag >good-tag &&
+	test_when_finished "git tag -d tag" &&
+	sed -e "s/$commit/$blob/" <good-tag >broken-tag &&
+	tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-commit tag (lone)' '
+	test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-commit tag (seen)' '
+	test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+	test_i18ngrep "not a commit" output
+'
+
+test_expect_success 'setup unexpected non-tree tag' '
+	git tag -a -m "tagged tree" tag $tree &&
+	git cat-file tag tag >good-tag &&
+	test_when_finished "git tag -d tag" &&
+	sed -e "s/$tree/$blob/" <good-tag >broken-tag &&
+	tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_success 'traverse unexpected non-tree tag (lone)' '
+	test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-tree tag (seen)' '
+	test_must_fail git rev-list --objects $blob $tag >output 2>&1 &&
+	test_i18ngrep "not a tree" output
+'
+
+test_expect_success 'setup unexpected non-blob tag' '
+	git tag -a -m "tagged blob" tag $blob &&
+	git cat-file tag tag >good-tag &&
+	test_when_finished "git tag -d tag" &&
+	sed -e "s/$blob/$commit/" <good-tag >broken-tag &&
+	tag=$(git hash-object -w --literally -t tag broken-tag)
+'
+
+test_expect_failure 'traverse unexpected non-blob tag (lone)' '
+	test_must_fail git rev-list --objects $tag
+'
+
+test_expect_success 'traverse unexpected non-blob tag (seen)' '
+	test_must_fail git rev-list --objects $commit $tag >output 2>&1 &&
+	test_i18ngrep "not a blob" output
+'
+
+test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 0ffd630..d923521 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -392,8 +392,15 @@
 test_atom head upstream:trackshort '>'
 test_atom head upstream:track,nobracket 'ahead 1'
 test_atom head upstream:nobracket,track 'ahead 1'
-test_atom head push:track '[ahead 1]'
-test_atom head push:trackshort '>'
+
+test_expect_success 'setup for push:track[short]' '
+	test_commit third &&
+	git update-ref refs/remotes/myfork/master master &&
+	git reset master~1
+'
+
+test_atom head push:track '[behind 1]'
+test_atom head push:trackshort '<'
 
 test_expect_success 'Check that :track[short] cannot be used with other atoms' '
 	test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
@@ -420,8 +427,10 @@
 test_expect_success 'set up color tests' '
 	cat >expected.color <<-EOF &&
 	$(git rev-parse --short refs/heads/master) <GREEN>master<RESET>
+	$(git rev-parse --short refs/remotes/myfork/master) <GREEN>myfork/master<RESET>
 	$(git rev-parse --short refs/remotes/origin/master) <GREEN>origin/master<RESET>
 	$(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
+	$(git rev-parse --short refs/tags/third) <GREEN>third<RESET>
 	$(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
 	EOF
 	sed "s/<[^>]*>//g" <expected.color >expected.bare &&
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 4684d06..7411bf7 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -120,6 +120,25 @@
 	test_must_be_empty stderr
 '
 
+test_expect_success 'gc.reflogExpire{Unreachable,}=never skips "expire" via "gc"' '
+	test_config gc.reflogExpire never &&
+	test_config gc.reflogExpireUnreachable never &&
+
+	GIT_TRACE=$(pwd)/trace.out git gc &&
+
+	# Check that git-pack-refs is run as a sanity check (done via
+	# gc_before_repack()) but that git-expire is not.
+	grep -E "^trace: (built-in|exec|run_command): git pack-refs --" trace.out &&
+	! grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
+'
+
+test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "expire" via "gc"' '
+	>trace.out &&
+	test_config gc.reflogExpire never &&
+	GIT_TRACE=$(pwd)/trace.out git gc &&
+	grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
+'
+
 run_and_wait_for_auto_gc () {
 	# We read stdout from gc for the side effect of waiting until the
 	# background gc process exits, closing its fd 9.  Furthermore, the
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 0b01862..e285686 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1700,6 +1700,17 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'recursive tagging should give advice' '
+	sed -e "s/|$//" <<-EOF >expect &&
+	hint: You have created a nested tag. The object referred to by your new is
+	hint: already a tag. If you meant to tag the object that it points to, use:
+	hint: |
+	hint: 	git tag -f nested annotated-v4.0^{}
+	EOF
+	git tag -m nested nested annotated-v4.0 2>actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'multiple --points-at are OR-ed together' '
 	cat >expect <<-\EOF &&
 	v2.0
diff --git a/t/t7113-post-index-change-hook.sh b/t/t7113-post-index-change-hook.sh
new file mode 100755
index 0000000..f011ad7
--- /dev/null
+++ b/t/t7113-post-index-change-hook.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+test_description='post index change hook'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	mkdir -p dir1 &&
+	touch dir1/file1.txt &&
+	echo testing >dir1/file2.txt &&
+	git add . &&
+	git commit -m "initial"
+'
+
+test_expect_success 'test status, add, commit, others trigger hook without flags set' '
+	mkdir -p .git/hooks &&
+	write_script .git/hooks/post-index-change <<-\EOF &&
+		if test "$1" -eq 1; then
+			echo "Invalid combination of flags passed to hook; updated_workdir is set." >testfailure
+			exit 1
+		fi
+		if test "$2" -eq 1; then
+			echo "Invalid combination of flags passed to hook; updated_skipworktree is set." >testfailure
+			exit 1
+		fi
+		if test -f ".git/index.lock"; then
+			echo ".git/index.lock exists" >testfailure
+			exit 3
+		fi
+		if ! test -f ".git/index"; then
+			echo ".git/index does not exist" >testfailure
+			exit 3
+		fi
+		echo "success" >testsuccess
+	EOF
+	mkdir -p dir2 &&
+	touch dir2/file1.txt &&
+	touch dir2/file2.txt &&
+	: force index to be dirty &&
+	test-tool chmtime +60 dir1/file1.txt &&
+	git status &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git add . &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git commit -m "second" &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git checkout -- dir1/file1.txt &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git update-index &&
+	test_path_is_missing testsuccess &&
+	test_path_is_missing testfailure &&
+	git reset --soft &&
+	test_path_is_missing testsuccess &&
+	test_path_is_missing testfailure
+'
+
+test_expect_success 'test checkout and reset trigger the hook' '
+	write_script .git/hooks/post-index-change <<-\EOF &&
+		if test "$1" -eq 1 && test "$2" -eq 1; then
+			echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+			exit 1
+		fi
+		if test "$1" -eq 0 && test "$2" -eq 0; then
+			echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+			exit 2
+		fi
+		if test "$1" -eq 1; then
+			if test -f ".git/index.lock"; then
+				echo "updated_workdir set but .git/index.lock exists" >testfailure
+				exit 3
+			fi
+			if ! test -f ".git/index"; then
+				echo "updated_workdir set but .git/index does not exist" >testfailure
+				exit 3
+			fi
+		else
+			echo "update_workdir should be set for checkout" >testfailure
+			exit 4
+		fi
+		echo "success" >testsuccess
+	EOF
+	: force index to be dirty &&
+	test-tool chmtime +60 dir1/file1.txt &&
+	git checkout master &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	test-tool chmtime +60 dir1/file1.txt &&
+	git checkout HEAD &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	test-tool chmtime +60 dir1/file1.txt &&
+	git reset --hard &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git checkout -B test &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure
+'
+
+test_expect_success 'test reset --mixed and update-index triggers the hook' '
+	write_script .git/hooks/post-index-change <<-\EOF &&
+		if test "$1" -eq 1 && test "$2" -eq 1; then
+			echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+			exit 1
+		fi
+		if test "$1" -eq 0 && test "$2" -eq 0; then
+			echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+			exit 2
+		fi
+		if test "$2" -eq 1; then
+			if test -f ".git/index.lock"; then
+				echo "updated_skipworktree set but .git/index.lock exists" >testfailure
+				exit 3
+			fi
+			if ! test -f ".git/index"; then
+				echo "updated_skipworktree set but .git/index does not exist" >testfailure
+				exit 3
+			fi
+		else
+			echo "updated_skipworktree should be set for reset --mixed and update-index" >testfailure
+			exit 4
+		fi
+		echo "success" >testsuccess
+	EOF
+	: force index to be dirty &&
+	test-tool chmtime +60 dir1/file1.txt &&
+	git reset --mixed --quiet HEAD~1 &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git hash-object -w --stdin <dir1/file2.txt >expect &&
+	git update-index --cacheinfo 100644 "$(cat expect)" dir1/file1.txt &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure &&
+	git update-index --skip-worktree dir1/file2.txt &&
+	git update-index --remove dir1/file2.txt &&
+	test_path_is_file testsuccess && rm -f testsuccess &&
+	test_path_is_missing testfailure
+'
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 72b9b37..5990299 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -223,13 +223,8 @@
 	test_must_fail git checkout simple 2>errs &&
 	test_i18ngrep overwritten errs &&
 
-	git checkout --merge simple 2>errs &&
-	test_i18ngrep ! overwritten errs &&
-	git ls-files -u &&
-	test_must_fail git cat-file -t :0:two &&
-	test "$(git cat-file -t :1:two)" = blob &&
-	test "$(git cat-file -t :2:two)" = blob &&
-	test_must_fail git cat-file -t :3:two
+	test_must_fail git read-tree --quiet -m -u HEAD simple 2>errs &&
+	test_must_be_empty errs
 '
 
 test_expect_success 'checkout to detach HEAD (with advice declined)' '
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index aba2d4d..a208cb2 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -46,6 +46,15 @@
 	test_must_fail git submodule init
 '
 
+test_expect_success 'add aborts on repository with no commits' '
+	cat >expect <<-\EOF &&
+	'"'repo-no-commits'"' does not have a commit checked out
+	EOF
+	git init repo-no-commits &&
+	test_must_fail git submodule add ../a ./repo-no-commits 2>actual &&
+	test_i18ncmp expect actual
+'
+
 test_expect_success 'setup - repository in init subdirectory' '
 	mkdir init &&
 	(
@@ -809,7 +818,7 @@
 		cp pristine-.git-config .git/config &&
 		cp pristine-.gitmodules .gitmodules &&
 		mkdir -p a/b/c &&
-		(cd a/b/c && git init) &&
+		(cd a/b/c && git init && test_commit msg) &&
 		git config remote.origin.url ../foo/bar.git &&
 		git submodule add ../bar/a/b/c ./a/b/c &&
 		git submodule init &&
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 77729ac..706ae76 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -411,4 +411,14 @@
 	test_cmp expected actual
 '
 
+test_expect_success 'option-like arguments passed to foreach commands are not lost' '
+	(
+		cd super &&
+		git submodule foreach "echo be --quiet" > ../expected &&
+		git submodule foreach echo be --quiet > ../actual
+	) &&
+	grep -sq -e "--quiet" expected &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index 89690b7..fcc0fb8 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -142,6 +142,15 @@
 	)
 '
 
+test_expect_success 'unsetting submodules config from the working tree with "submodule--helper config --unset"' '
+	(cd super &&
+		git submodule--helper config --unset submodule.submodule.url &&
+		git submodule--helper config submodule.submodule.url >actual &&
+		test_must_be_empty actual
+	)
+'
+
+
 test_expect_success 'writing submodules config with "submodule--helper config"' '
 	(cd super &&
 		echo "new_url" >expect &&
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
new file mode 100755
index 0000000..c4b370e
--- /dev/null
+++ b/t/t7419-submodule-set-branch.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# Copyright (c) 2019 Denton Liu
+#
+
+test_description='Test submodules set-branch subcommand
+
+This test verifies that the set-branch subcommand of git-submodule is working
+as expected.
+'
+
+TEST_NO_CREATE_REPO=1
+. ./test-lib.sh
+
+test_expect_success 'submodule config cache setup' '
+	mkdir submodule &&
+	(cd submodule &&
+		git init &&
+		echo a >a &&
+		git add . &&
+		git commit -ma &&
+		git checkout -b topic &&
+		echo b >a &&
+		git add . &&
+		git commit -mb
+	) &&
+	mkdir super &&
+	(cd super &&
+		git init &&
+		git submodule add ../submodule &&
+		git commit -m "add submodule"
+	)
+'
+
+test_expect_success 'ensure submodule branch is unset' '
+	(cd super &&
+		test_must_fail grep branch .gitmodules
+	)
+'
+
+test_expect_success 'test submodule set-branch --branch' '
+	(cd super &&
+		git submodule set-branch --branch topic submodule &&
+		grep "branch = topic" .gitmodules &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		b
+		EOF
+		git -C submodule show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'test submodule set-branch --default' '
+	(cd super &&
+		git submodule set-branch --default submodule &&
+		test_must_fail grep branch .gitmodules &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		a
+		EOF
+		git -C submodule show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'test submodule set-branch -b' '
+	(cd super &&
+		git submodule set-branch -b topic submodule &&
+		grep "branch = topic" .gitmodules &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		b
+		EOF
+		git -C submodule show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'test submodule set-branch -d' '
+	(cd super &&
+		git submodule set-branch -d submodule &&
+		test_must_fail grep branch .gitmodules &&
+		git submodule update --remote &&
+		cat <<-\EOF >expect &&
+		a
+		EOF
+		git -C submodule show -s --pretty=%s >actual &&
+		test_cmp expect actual
+	)
+'
+
+test_done
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index ca4a740..5733d9c 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -16,7 +16,8 @@
 # Arguments: [<prefix] [<commit message>] [<commit options>]
 check_summary_oneline() {
 	test_tick &&
-	git commit ${3+"$3"} -m "$2" | head -1 > act &&
+	git commit ${3+"$3"} -m "$2" >raw &&
+	head -n 1 raw >act &&
 
 	# branch name
 	SUMMARY_PREFIX="$(git name-rev --name-only HEAD)" &&
@@ -68,7 +69,7 @@
 	git checkout recursive-a &&
 	test_must_fail git merge recursive-b &&
 	# resolve the conflict
-	echo commit-a > file1 &&
+	echo commit-a >file1 &&
 	git add file1 &&
 	check_summary_oneline "" "Merge"
 '
@@ -142,9 +143,11 @@
 	>positive &&
 	git add positive &&
 	git commit -s -m "thank you" &&
-	actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") &&
-	expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") &&
-	test "z$actual" = "z$expected"
+	git cat-file commit HEAD >commit.msg &&
+	sed -ne "s/Signed-off-by: //p" commit.msg >actual &&
+	git var GIT_COMMITTER_IDENT >ident &&
+	sed -e "s/>.*/>/" ident >expected &&
+	test_cmp expected actual
 
 '
 
@@ -153,8 +156,8 @@
 	>negative &&
 	git add negative &&
 	git commit -m "one" -m "two" -m "three" &&
-	actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") &&
-	expected=$(echo one; echo; echo two; echo; echo three) &&
+	actual=$(git cat-file commit HEAD >tmp && sed -e "1,/^\$/d" tmp && rm tmp) &&
+	expected=$(test_write_lines "one" "" "two" "" "three") &&
 	test "z$actual" = "z$expected"
 
 '
@@ -163,7 +166,8 @@
 
 	echo minus >negative &&
 	git add negative &&
-	git status -v | sed -ne "/^diff --git /p" >actual &&
+	git status -v >raw &&
+	sed -ne "/^diff --git /p" raw >actual &&
 	echo "diff --git a/negative b/negative" >expect &&
 	test_cmp expect actual
 
@@ -189,7 +193,8 @@
 
 	echo >>negative &&
 	git commit --cleanup=verbatim --no-status -t expect -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d" >actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -198,7 +203,8 @@
 
 	echo >>negative &&
 	git commit --cleanup=verbatim -F expect -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -207,7 +213,8 @@
 
 	echo >>negative &&
 	git commit --cleanup=verbatim -m "$mesg_with_comment_and_newlines" -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -215,10 +222,11 @@
 test_expect_success 'cleanup commit messages (whitespace option,-F)' '
 
 	echo >>negative &&
-	{ echo;echo "# text";echo; } >text &&
+	test_write_lines "" "# text" "" >text &&
 	echo "# text" >expect &&
 	git commit --cleanup=whitespace -F text -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -226,48 +234,51 @@
 test_expect_success 'cleanup commit messages (scissors option,-F,-e)' '
 
 	echo >>negative &&
-	cat >text <<EOF &&
+	cat >text <<-\EOF &&
 
-# to be kept
+	# to be kept
 
-  # ------------------------ >8 ------------------------
-# to be kept, too
-# ------------------------ >8 ------------------------
-to be removed
-# ------------------------ >8 ------------------------
-to be removed, too
-EOF
+	  # ------------------------ >8 ------------------------
+	# to be kept, too
+	# ------------------------ >8 ------------------------
+	to be removed
+	# ------------------------ >8 ------------------------
+	to be removed, too
+	EOF
 
-	cat >expect <<EOF &&
-# to be kept
+	cat >expect <<-\EOF &&
+	# to be kept
 
-  # ------------------------ >8 ------------------------
-# to be kept, too
-EOF
+	  # ------------------------ >8 ------------------------
+	# to be kept, too
+	EOF
 	git commit --cleanup=scissors -e -F text -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >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
+	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 &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_must_be_empty actual
 '
 
 test_expect_success 'cleanup commit messages (strip option,-F)' '
 
 	echo >>negative &&
-	{ echo;echo "# text";echo sample;echo; } >text &&
+	test_write_lines "" "# text" "sample" "" >text &&
 	echo sample >expect &&
 	git commit --cleanup=strip -F text -a &&
-	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	git cat-file -p HEAD >raw &&
+	sed -e "1,/^\$/d" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -275,7 +286,7 @@
 test_expect_success 'cleanup commit messages (strip option,-F,-e)' '
 
 	echo >>negative &&
-	{ echo;echo sample;echo; } >text &&
+	test_write_lines "" "sample" "" >text &&
 	git commit -e -F text -a &&
 	head -n 4 .git/COMMIT_EDITMSG >actual
 '
@@ -387,7 +398,7 @@
 '
 
 write_script .git/FAKE_EDITOR <<EOF
-echo editor started > "$(pwd)/.git/result"
+echo editor started >"$(pwd)/.git/result"
 exit 0
 EOF
 
@@ -455,7 +466,7 @@
 test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
 	echo >>negative &&
 	! "$SHELL_PATH" -c '\''
-	  echo kill -TERM $$ >> .git/FAKE_EDITOR
+	  echo kill -TERM $$ >>.git/FAKE_EDITOR
 	  GIT_EDITOR=.git/FAKE_EDITOR
 	  export GIT_EDITOR
 	  exec git commit -a'\'' &&
@@ -471,7 +482,8 @@
 	test_must_fail git merge second master &&
 	git checkout master g &&
 	EDITOR=: git commit -a &&
-	git cat-file commit HEAD | sed -n -e "s/^parent //p" -e "/^$/q" >actual &&
+	git cat-file commit HEAD >raw &&
+	sed -n -e "s/^parent //p" -e "/^$/q" raw >actual &&
 	test_cmp expect actual
 
 '
@@ -480,7 +492,8 @@
 
 	git reset --hard &&
 	git commit -s -m "hello: kitty" --allow-empty &&
-	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
 	test_line_count = 3 actual
 
 '
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 1061482..7f9c68c 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -233,20 +233,65 @@
 	cat result.9z >file &&
 	git commit --no-edit -a &&
 
-	{
-		cat <<-EOF
-		Squashed commit of the following:
+	cat >expect <<-EOF &&
+	Squashed commit of the following:
 
-		$(git show -s c7)
+	$(git show -s c7)
 
-		# Conflicts:
-		#	file
-		EOF
-	} >expect &&
-	git cat-file commit HEAD | sed -e '1,/^$/d' >actual &&
+	# Conflicts:
+	#	file
+	EOF
+	git cat-file commit HEAD >raw &&
+	sed -e '1,/^$/d' raw >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
+	git config commit.cleanup scissors &&
+	git reset --hard c3 &&
+	test_must_fail git merge c7 &&
+	cat result.9z >file &&
+	git commit --no-edit -a &&
+
+	cat >expect <<-\EOF &&
+	Merge tag '"'"'c7'"'"'
+
+	# ------------------------ >8 ------------------------
+	# Do not modify or remove the line above.
+	# Everything below it will be ignored.
+	#
+	# Conflicts:
+	#	file
+	EOF
+	git cat-file commit HEAD >raw &&
+	sed -e '1,/^$/d' raw >actual &&
+	test_i18ncmp expect actual
+'
+
+test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
+	git config commit.cleanup scissors &&
+	git reset --hard c3 &&
+	test_must_fail git merge --squash c7 &&
+	cat result.9z >file &&
+	git commit --no-edit -a &&
+
+	cat >expect <<-EOF &&
+	Squashed commit of the following:
+
+	$(git show -s c7)
+
+	# ------------------------ >8 ------------------------
+	# Do not modify or remove the line above.
+	# Everything below it will be ignored.
+	#
+	# Conflicts:
+	#	file
+	EOF
+	git cat-file commit HEAD >raw &&
+	sed -e '1,/^$/d' raw >actual &&
+	test_i18ncmp expect actual
+'
+
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 and c3' '
@@ -680,10 +725,10 @@
 (
 	echo "Merge work done on the side branch c1"
 	echo
-	cat <"$1"
+	cat "$1"
 ) >"$1.tmp" && mv "$1.tmp" "$1"
 # strip comments and blank lines from end of message
-sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected
+sed -e '/^#/d' "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' >expected
 EOF
 chmod 755 editor
 
@@ -768,14 +813,14 @@
 	git commit -m base &&
 
 	# one side changes the first line of each to "master"
-	sed s/-1/-master/ <file >tmp &&
+	sed s/-1/-master/ file >tmp &&
 	mv tmp file &&
 	git commit -am master &&
 
 	# and the other to "side"; merging the two will
 	# yield 256 separate conflicts
 	git checkout -b side HEAD^ &&
-	sed s/-1/-side/ <file >tmp &&
+	sed s/-1/-side/ file >tmp &&
 	mv tmp file &&
 	git commit -am side
 '
@@ -814,7 +859,7 @@
 test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '
 	git reset --hard c0 &&
 	! "$SHELL_PATH" -c '\''
-	  echo kill -TERM $$ >> .git/FAKE_EDITOR
+	  echo kill -TERM $$ >>.git/FAKE_EDITOR
 	  GIT_EDITOR=.git/FAKE_EDITOR
 	  export GIT_EDITOR
 	  exec git merge --no-ff --edit c1'\'' &&
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
index 89619cf..cd4f960 100755
--- a/t/t7604-merge-custom-message.sh
+++ b/t/t7604-merge-custom-message.sh
@@ -16,16 +16,16 @@
 }
 
 test_expect_success 'setup' '
-	echo c0 > c0.c &&
+	echo c0 >c0.c &&
 	git add c0.c &&
 	git commit -m c0 &&
 	git tag c0 &&
-	echo c1 > c1.c &&
+	echo c1 >c1.c &&
 	git add c1.c &&
 	git commit -m c1 &&
 	git tag c1 &&
 	git reset --hard c0 &&
-	echo c2 > c2.c &&
+	echo c2 >c2.c &&
 	git add c2.c &&
 	git commit -m c2 &&
 	git tag c2 &&
@@ -36,15 +36,80 @@
 test_expect_success 'merge c2 with a custom message' '
 	git reset --hard c1 &&
 	git merge -m "$(cat exp.subject)" c2 &&
-	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
 	test_cmp exp.subject actual
 '
 
 test_expect_success 'merge --log appends to custom message' '
 	git reset --hard c1 &&
 	git merge --log -m "$(cat exp.subject)" c2 &&
-	git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
 	test_cmp exp.log actual
 '
 
+mesg_with_comment_and_newlines='
+# text
+
+'
+
+test_expect_success 'prepare file with comment line and trailing newlines'  '
+	printf "%s" "$mesg_with_comment_and_newlines" >expect
+'
+
+test_expect_success 'cleanup commit messages (verbatim option)' '
+	git reset --hard c1 &&
+	git merge --cleanup=verbatim -F expect c2 &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cleanup commit messages (whitespace option)' '
+	git reset --hard c1 &&
+	test_write_lines "" "# text" "" >text &&
+	echo "# text" >expect &&
+	git merge --cleanup=whitespace -F text c2 &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cleanup merge messages (scissors option)' '
+	git reset --hard c1 &&
+	cat >text <<-\EOF &&
+
+	# to be kept
+
+	  # ------------------------ >8 ------------------------
+	# to be kept, too
+	# ------------------------ >8 ------------------------
+	to be removed
+	# ------------------------ >8 ------------------------
+	to be removed, too
+	EOF
+
+	cat >expect <<-\EOF &&
+	# to be kept
+
+	  # ------------------------ >8 ------------------------
+	# to be kept, too
+	EOF
+	git merge --cleanup=scissors -e -F text c2 &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'cleanup commit messages (strip option)' '
+	git reset --hard c1 &&
+	test_write_lines "" "# text" "sample" "" >text &&
+	echo sample >expect &&
+	git merge --cleanup=strip -F text c2 &&
+	git cat-file commit HEAD >raw &&
+	sed -e "1,/^$/d" raw >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 6162e2a..86d0516 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -221,5 +221,22 @@
 	)
 '
 
-test_done
+test_expect_success 'bitmaps are created by default in bare repos' '
+	git clone --bare .git bare.git &&
+	git -C bare.git repack -ad &&
+	bitmap=$(ls bare.git/objects/pack/*.bitmap) &&
+	test_path_is_file "$bitmap"
+'
 
+test_expect_success 'incremental repack does not complain' '
+	git -C bare.git repack -q 2>repack.err &&
+	test_must_be_empty repack.err
+'
+
+test_expect_success 'bitmaps can be disabled on bare repos' '
+	git -c repack.writeBitmaps=false -C bare.git repack -ad &&
+	bitmap=$(ls bare.git/objects/pack/*.bitmap 2>/dev/null || :) &&
+	test -z "$bitmap"
+'
+
+test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index bb9a7f4..480dd06 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -705,4 +705,14 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'outside worktree' '
+	echo 1 >1 &&
+	echo 2 >2 &&
+	test_expect_code 1 nongit git \
+		-c diff.tool=echo -c difftool.echo.cmd="echo \$LOCAL \$REMOTE" \
+		difftool --no-prompt --no-index ../1 ../2 >actual &&
+	echo "../1 ../2" >expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index fa475d5..134a6945 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -380,11 +380,7 @@
 	fi
 '
 
-# Recursing down into nested submodules which do not have .gitmodules in their
-# working tree does not work yet. This is because config_from_gitmodules()
-# uses get_oid() and the latter is still not able to get objects from an
-# arbitrary repository (the nested submodule, in this case).
-test_expect_failure 'grep --recurse-submodules with submodules without .gitmodules in the working tree' '
+test_expect_success 'grep --recurse-submodules with submodules without .gitmodules in the working tree' '
 	test_when_finished "git -C submodule checkout .gitmodules" &&
 	rm submodule/.gitmodules &&
 	git grep --recurse-submodules -e "(.|.)[\d]" >actual &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index ee1efcc..1e3ac3c 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -481,6 +481,20 @@
 	grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
 '
 
+test_expect_success $PREREQ 'carriage returns with auto encoding are quoted-printable' '
+	clean_fake_sendmail &&
+	cp $patches cr.patch &&
+	printf "this is a line\r\n" >>cr.patch &&
+	git send-email \
+		--from="Example <nobody@example.com>" \
+		--to=nobody@example.com \
+		--smtp-server="$(pwd)/fake.sendmail" \
+		--transfer-encoding=auto \
+		--no-validate \
+		cr.patch &&
+	grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
+'
+
 for enc in auto quoted-printable base64
 do
 	test_expect_success $PREREQ "--validate passes with encoding $enc" '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 64bb495..9b44a44 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -120,6 +120,4 @@
 	git svn dcommit
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
index 41a0266..a159ff9 100755
--- a/t/t9118-git-svn-funky-branch-names.sh
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -87,6 +87,4 @@
 	)
 	'
 
-stop_httpd
-
 test_done
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index b28a174..40b714d 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -74,6 +74,4 @@
 	)
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
index 9ee23be..a307305 100755
--- a/t/t9142-git-svn-shallow-clone.sh
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -26,6 +26,4 @@
 	( cd g && git rev-parse --symbolic --verify HEAD )
 '
 
-stop_httpd
-
 test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 59a13b6..3668263 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -3262,4 +3262,41 @@
 	background_import_still_running
 '
 
+###
+### series W (get-mark and empty orphan commits)
+###
+
+cat >>W-input <<-W_INPUT_END
+	commit refs/heads/W-branch
+	mark :1
+	author Full Name <user@company.tld> 1000000000 +0100
+	committer Full Name <user@company.tld> 1000000000 +0100
+	data 27
+	Intentionally empty commit
+	LFsget-mark :1
+	W_INPUT_END
+
+test_expect_success !MINGW 'W: get-mark & empty orphan commit with no newlines' '
+	sed -e s/LFs// W-input | tr L "\n" | git fast-import
+'
+
+test_expect_success !MINGW 'W: get-mark & empty orphan commit with one newline' '
+	sed -e s/LFs/L/ W-input | tr L "\n" | git fast-import
+'
+
+test_expect_success !MINGW 'W: get-mark & empty orphan commit with ugly second newline' '
+	# Technically, this should fail as it has too many linefeeds
+	# according to the grammar in fast-import.txt.  But, for whatever
+	# reason, it works.  Since using the correct number of newlines
+	# does not work with older (pre-2.22) versions of git, allow apps
+	# that used this second-newline workaround to keep working by
+	# checking it with this test...
+	sed -e s/LFs/LL/ W-input | tr L "\n" | git fast-import
+'
+
+test_expect_success !MINGW 'W: get-mark & empty orphan commit with erroneous third newline' '
+	# ...but do NOT allow more empty lines than that (see previous test).
+	sed -e s/LFs/LLL/ W-input | tr L "\n" | test_must_fail git fast-import
+'
+
 test_done
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index 729cd25..5856563 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -326,8 +326,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 6a86d69..38d6b90 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -151,7 +151,7 @@
 '
 
 test_expect_success 'restart p4d' '
-	kill_p4d &&
+	stop_and_cleanup_p4d &&
 	start_p4d
 '
 
@@ -505,7 +505,7 @@
 '
 
 test_expect_success 'restart p4d' '
-	kill_p4d &&
+	stop_and_cleanup_p4d &&
 	start_p4d
 '
 
@@ -610,8 +610,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh
index 9978352..94edebe 100755
--- a/t/t9802-git-p4-filetype.sh
+++ b/t/t9802-git-p4-filetype.sh
@@ -333,8 +333,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh
index d5c3675..2913277 100755
--- a/t/t9803-git-p4-shell-metachars.sh
+++ b/t/t9803-git-p4-shell-metachars.sh
@@ -105,8 +105,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh
index e30f80e..3236457 100755
--- a/t/t9804-git-p4-label.sh
+++ b/t/t9804-git-p4-label.sh
@@ -108,8 +108,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh
index 5fbf904..90ef647 100755
--- a/t/t9805-git-p4-skip-submit-edit.sh
+++ b/t/t9805-git-p4-skip-submit-edit.sh
@@ -98,8 +98,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
index 3f5291b..4e794a0 100755
--- a/t/t9806-git-p4-options.sh
+++ b/t/t9806-git-p4-options.sh
@@ -300,9 +300,4 @@
 	test_path_is_file "$git"/cli_file2.t
 '
 
-
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index 850d979..eaaae41 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -593,8 +593,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh
index 11d2b51..58a9b3b 100755
--- a/t/t9808-git-p4-chdir.sh
+++ b/t/t9808-git-p4-chdir.sh
@@ -83,8 +83,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh
index 897b3c3..3cff1fc 100755
--- a/t/t9809-git-p4-client-view.sh
+++ b/t/t9809-git-p4-client-view.sh
@@ -836,8 +836,4 @@
 	git_verify "cdir 1/file11" "cdir 1/file12"
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh
index cc53deb..57b533d 100755
--- a/t/t9810-git-p4-rcs.sh
+++ b/t/t9810-git-p4-rcs.sh
@@ -360,8 +360,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index 602b0a5..c1446f2 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -191,7 +191,7 @@
 	(
 		cd "$cli" &&
 		p4 sync ... &&
-		!(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+		! p4 labels | grep GIT_TAG_ON_A_BRANCH
 	)
 '
 
@@ -259,9 +259,4 @@
 	)
 '
 
-
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh
index 0206771..254a7c2 100755
--- a/t/t9812-git-p4-wildcards.sh
+++ b/t/t9812-git-p4-wildcards.sh
@@ -211,8 +211,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh
index 783c6ad..fd018c8 100755
--- a/t/t9813-git-p4-preserve-users.sh
+++ b/t/t9813-git-p4-preserve-users.sh
@@ -138,8 +138,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh
index 60baa06..468767c 100755
--- a/t/t9814-git-p4-rename.sh
+++ b/t/t9814-git-p4-rename.sh
@@ -242,8 +242,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh
index eaf03a6..9779dc0 100755
--- a/t/t9815-git-p4-submit-fail.sh
+++ b/t/t9815-git-p4-submit-fail.sh
@@ -422,8 +422,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh
index d048bd3..9328410 100755
--- a/t/t9816-git-p4-locked.sh
+++ b/t/t9816-git-p4-locked.sh
@@ -138,8 +138,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh
index aac568e..96d25f0 100755
--- a/t/t9817-git-p4-exclude.sh
+++ b/t/t9817-git-p4-exclude.sh
@@ -64,8 +64,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh
index ce7cb22..0db7ab9 100755
--- a/t/t9818-git-p4-block.sh
+++ b/t/t9818-git-p4-block.sh
@@ -146,8 +146,4 @@
 	test_line_count \> 10 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
index d808c00..600ce1e 100755
--- a/t/t9819-git-p4-case-folding.sh
+++ b/t/t9819-git-p4-case-folding.sh
@@ -53,8 +53,4 @@
 	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
index 3c22f74..fa1bba1 100755
--- a/t/t9820-git-p4-editor-handling.sh
+++ b/t/t9820-git-p4-editor-handling.sh
@@ -31,8 +31,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9821-git-p4-path-variations.sh b/t/t9821-git-p4-path-variations.sh
index 81e46ac..ef80f16 100755
--- a/t/t9821-git-p4-path-variations.sh
+++ b/t/t9821-git-p4-path-variations.sh
@@ -193,8 +193,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh
index c78477c..572d395 100755
--- a/t/t9822-git-p4-path-encoding.sh
+++ b/t/t9822-git-p4-path-encoding.sh
@@ -7,6 +7,13 @@
 UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt"
 ISO8859_ESCAPED="a-\344_o-\366_u-\374.txt"
 
+ISO8859="$(printf "$ISO8859_ESCAPED")" &&
+echo content123 >"$ISO8859" &&
+rm "$ISO8859" || {
+	skip_all="fs does not accept ISO-8859-1 filenames"
+	test_done
+}
+
 test_expect_success 'start p4d' '
 	start_p4d
 '
@@ -67,8 +74,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh
index 1f2dc36..88b76dc 100755
--- a/t/t9823-git-p4-mock-lfs.sh
+++ b/t/t9823-git-p4-mock-lfs.sh
@@ -185,8 +185,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh
index ed80ca8..a28dbbd 100755
--- a/t/t9824-git-p4-git-lfs.sh
+++ b/t/t9824-git-p4-git-lfs.sh
@@ -287,8 +287,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh
index 1551845..f049ff8 100755
--- a/t/t9825-git-p4-handle-utf16-without-bom.sh
+++ b/t/t9825-git-p4-handle-utf16-without-bom.sh
@@ -43,8 +43,4 @@
 	git p4 clone --dest="$git" //depot
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh
index fa8b9da..fd64afe 100755
--- a/t/t9826-git-p4-keep-empty-commits.sh
+++ b/t/t9826-git-p4-keep-empty-commits.sh
@@ -127,8 +127,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh
index 7433998..d3670bd 100755
--- a/t/t9827-git-p4-change-filetype.sh
+++ b/t/t9827-git-p4-change-filetype.sh
@@ -59,8 +59,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh
index e20395c..ca6c294 100755
--- a/t/t9828-git-p4-map-user.sh
+++ b/t/t9828-git-p4-map-user.sh
@@ -54,8 +54,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh
index 971aeee..88cfb1f 100755
--- a/t/t9829-git-p4-jobs.sh
+++ b/t/t9829-git-p4-jobs.sh
@@ -92,8 +92,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh
index 2ad1b08..3fb6960 100755
--- a/t/t9830-git-p4-symlink-dir.sh
+++ b/t/t9830-git-p4-symlink-dir.sh
@@ -36,8 +36,4 @@
 
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh
index be44c97..d743ca3 100755
--- a/t/t9831-git-p4-triggers.sh
+++ b/t/t9831-git-p4-triggers.sh
@@ -96,8 +96,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
 test_done
diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh
index 41c09f1..1286a5b 100755
--- a/t/t9832-unshelve.sh
+++ b/t/t9832-unshelve.sh
@@ -174,8 +174,5 @@
 		test_path_is_file file_to_shelve
 	)
 '
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
 
 test_done
diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh
index 47b312e..e22369c 100755
--- a/t/t9833-errors.sh
+++ b/t/t9833-errors.sh
@@ -45,9 +45,4 @@
 	)
 '
 
-test_expect_success 'kill p4d' '
-	kill_p4d
-'
-
-
 test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 681c41b..8270de7 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -943,6 +943,34 @@
 		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
 }
 
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test script, e.g. to stop a daemon:
+#
+#	test_expect_success 'test git daemon' '
+#		git daemon &
+#		daemon_pid=$! &&
+#		test_atexit 'kill $daemon_pid' &&
+#		hello world
+#	'
+#
+# The commands will be executed before the trash directory is removed,
+# i.e. the atexit commands will still be able to access any pidfiles or
+# socket files.
+#
+# Note that these commands will be run even when a test script run
+# with '--immediate' fails.  Be careful with your atexit commands to
+# minimize any changes to the failed state.
+
+test_atexit () {
+	# We cannot detect when we are in a subshell in general, but by
+	# doing so on Bash is better than nothing (the test will
+	# silently pass on other shells).
+	test "${BASH_SUBSHELL-0}" = 0 ||
+	error "bug in test script: test_atexit does nothing in a subshell"
+	test_atexit_cleanup="{ $*
+		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+}
+
 # Most tests can use the created repository, but some may need to create more.
 # Usage: test_create_repo <directory>
 test_create_repo () {
@@ -1211,6 +1239,12 @@
 	'
 }
 
+# Converts base-16 data into base-8. The output is given as a sequence of
+# escaped octals, suitable for consumption by 'printf'.
+hex2oct () {
+	perl -ne 'printf "\\%03o", hex for /../g'
+}
+
 # Set the hash algorithm in use to $1.  Only useful when testing the testsuite.
 test_set_hash () {
 	test_hash_algo="$1"
diff --git a/t/test-lib.sh b/t/test-lib.sh
index c14ebe6..908ddb9 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -386,6 +386,7 @@
 	my @env = keys %ENV;
 	my $ok = join("|", qw(
 		TRACE
+		TR2_
 		DEBUG
 		TEST
 		.*_TEST
@@ -634,6 +635,10 @@
 
 die () {
 	code=$?
+	# This is responsible for running the atexit commands even when a
+	# test script run with '--immediate' fails, or when the user hits
+	# ctrl-C, i.e. when 'test_done' is not invoked at all.
+	test_atexit_handler || code=$?
 	if test -n "$GIT_EXIT_OK"
 	then
 		exit $code
@@ -645,7 +650,10 @@
 
 GIT_EXIT_OK=
 trap 'die' EXIT
-trap 'exit $?' INT TERM HUP
+# Disable '-x' tracing, because with some shells, notably dash, it
+# prevents running the cleanup commands when a test script run with
+# '--verbose-log -x' is interrupted.
+trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP
 
 # The user-facing functions are loaded from a separate file so that
 # test_perf subshells can have them too
@@ -1056,9 +1064,28 @@
 	junit_have_testcase=t
 }
 
+test_atexit_cleanup=:
+test_atexit_handler () {
+	# In a succeeding test script 'test_atexit_handler' is invoked
+	# twice: first from 'test_done', then from 'die' in the trap on
+	# EXIT.
+	# This condition and resetting 'test_atexit_cleanup' below makes
+	# sure that the registered cleanup commands are run only once.
+	test : != "$test_atexit_cleanup" || return 0
+
+	setup_malloc_check
+	test_eval_ "$test_atexit_cleanup"
+	test_atexit_cleanup=:
+	teardown_malloc_check
+}
+
 test_done () {
 	GIT_EXIT_OK=t
 
+	# Run the atexit commands _before_ the trash directory is
+	# removed, so the commands can access pidfiles and socket files.
+	test_atexit_handler
+
 	if test -n "$write_junit_xml" && test -n "$junit_xml_path"
 	then
 		test -n "$junit_have_testcase" || {
@@ -1412,6 +1439,7 @@
 if test -n "$GIT_TEST_GETTEXT_POISON_ORIG"
 then
 	GIT_TEST_GETTEXT_POISON=$GIT_TEST_GETTEXT_POISON_ORIG
+	export GIT_TEST_GETTEXT_POISON
 	unset GIT_TEST_GETTEXT_POISON_ORIG
 fi
 
diff --git a/tag.h b/tag.h
index e669c3e..03265fb 100644
--- a/tag.h
+++ b/tag.h
@@ -11,13 +11,13 @@
 	char *tag;
 	timestamp_t date;
 };
-extern struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
-extern int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
-extern int parse_tag(struct tag *item);
-extern void release_tag_memory(struct tag *t);
-extern struct object *deref_tag(struct repository *r, struct object *, const char *, int);
-extern struct object *deref_tag_noverify(struct object *);
-extern int gpg_verify_tag(const struct object_id *oid,
-		const char *name_to_report, unsigned flags);
+struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
+int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
+int parse_tag(struct tag *item);
+void release_tag_memory(struct tag *t);
+struct object *deref_tag(struct repository *r, struct object *, const char *, int);
+struct object *deref_tag_noverify(struct object *);
+int gpg_verify_tag(const struct object_id *oid,
+		   const char *name_to_report, unsigned flags);
 
 #endif /* TAG_H */
diff --git a/tempfile.h b/tempfile.h
index 61d8dc4..cddda0a 100644
--- a/tempfile.h
+++ b/tempfile.h
@@ -89,7 +89,7 @@
  * a tempfile (whose "fd" member can be used for writing to it), or
  * NULL on error. It is an error if a file already exists at that path.
  */
-extern struct tempfile *create_tempfile(const char *path);
+struct tempfile *create_tempfile(const char *path);
 
 /*
  * Register an existing file as a tempfile, meaning that it will be
@@ -97,7 +97,7 @@
  * but it can be worked with like any other closed tempfile (for
  * example, it can be opened using reopen_tempfile()).
  */
-extern struct tempfile *register_tempfile(const char *path);
+struct tempfile *register_tempfile(const char *path);
 
 
 /*
@@ -136,8 +136,8 @@
  */
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *mks_tempfile_sm(const char *filename_template,
-					int suffixlen, int mode);
+struct tempfile *mks_tempfile_sm(const char *filename_template,
+				 int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *mks_tempfile_s(const char *filename_template,
@@ -159,8 +159,8 @@
 }
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *mks_tempfile_tsm(const char *filename_template,
-					 int suffixlen, int mode);
+struct tempfile *mks_tempfile_tsm(const char *filename_template,
+				  int suffixlen, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *mks_tempfile_ts(const char *filename_template,
@@ -182,7 +182,7 @@
 }
 
 /* See "mks_tempfile functions" above. */
-extern struct tempfile *xmks_tempfile_m(const char *filename_template, int mode);
+struct tempfile *xmks_tempfile_m(const char *filename_template, int mode);
 
 /* See "mks_tempfile functions" above. */
 static inline struct tempfile *xmks_tempfile(const char *filename_template)
@@ -196,7 +196,7 @@
  * stream is closed automatically when `close_tempfile_gently()` is called or
  * when the file is deleted or renamed.
  */
-extern FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
+FILE *fdopen_tempfile(struct tempfile *tempfile, const char *mode);
 
 static inline int is_tempfile_active(struct tempfile *tempfile)
 {
@@ -207,10 +207,10 @@
  * Return the path of the lockfile. The return value is a pointer to a
  * field within the lock_file object and should not be freed.
  */
-extern const char *get_tempfile_path(struct tempfile *tempfile);
+const char *get_tempfile_path(struct tempfile *tempfile);
 
-extern int get_tempfile_fd(struct tempfile *tempfile);
-extern FILE *get_tempfile_fp(struct tempfile *tempfile);
+int get_tempfile_fd(struct tempfile *tempfile);
+FILE *get_tempfile_fp(struct tempfile *tempfile);
 
 /*
  * If the temporary file is still open, close it (and the file pointer
@@ -220,7 +220,7 @@
  * should eventually be called regardless of whether `close_tempfile_gently()`
  * succeeds.
  */
-extern int close_tempfile_gently(struct tempfile *tempfile);
+int close_tempfile_gently(struct tempfile *tempfile);
 
 /*
  * Re-open a temporary file that has been closed using
@@ -241,7 +241,7 @@
  *
  * * `rename_tempfile()` to move the file to its permanent location.
  */
-extern int reopen_tempfile(struct tempfile *tempfile);
+int reopen_tempfile(struct tempfile *tempfile);
 
 /*
  * Close the file descriptor and/or file pointer and remove the
@@ -249,7 +249,7 @@
  * `delete_tempfile()` for a `tempfile` object that has already been
  * deleted or renamed.
  */
-extern void delete_tempfile(struct tempfile **tempfile_p);
+void delete_tempfile(struct tempfile **tempfile_p);
 
 /*
  * Close the file descriptor and/or file pointer if they are still
@@ -260,6 +260,6 @@
  * `rename(2)`. It is a bug to call `rename_tempfile()` for a
  * `tempfile` object that is not currently active.
  */
-extern int rename_tempfile(struct tempfile **tempfile_p, const char *path);
+int rename_tempfile(struct tempfile **tempfile_p, const char *path);
 
 #endif /* TEMPFILE_H */
diff --git a/trace.h b/trace.h
index 171b256..9fa3e7a 100644
--- a/trace.h
+++ b/trace.h
@@ -17,34 +17,34 @@
 extern struct trace_key trace_perf_key;
 extern struct trace_key trace_setup_key;
 
-extern void trace_repo_setup(const char *prefix);
-extern int trace_want(struct trace_key *key);
-extern void trace_disable(struct trace_key *key);
-extern uint64_t getnanotime(void);
-extern void trace_command_performance(const char **argv);
-extern void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
+void trace_repo_setup(const char *prefix);
+int trace_want(struct trace_key *key);
+void trace_disable(struct trace_key *key);
+uint64_t getnanotime(void);
+void trace_command_performance(const char **argv);
+void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
 uint64_t trace_performance_enter(void);
 
 #ifndef HAVE_VARIADIC_MACROS
 
 __attribute__((format (printf, 1, 2)))
-extern void trace_printf(const char *format, ...);
+void trace_printf(const char *format, ...);
 
 __attribute__((format (printf, 2, 3)))
-extern void trace_printf_key(struct trace_key *key, const char *format, ...);
+void trace_printf_key(struct trace_key *key, const char *format, ...);
 
 __attribute__((format (printf, 2, 3)))
-extern void trace_argv_printf(const char **argv, const char *format, ...);
+void trace_argv_printf(const char **argv, const char *format, ...);
 
-extern void trace_strbuf(struct trace_key *key, const struct strbuf *data);
+void trace_strbuf(struct trace_key *key, const struct strbuf *data);
 
 /* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
 __attribute__((format (printf, 2, 3)))
-extern void trace_performance(uint64_t nanos, const char *format, ...);
+void trace_performance(uint64_t nanos, const char *format, ...);
 
 /* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
 __attribute__((format (printf, 2, 3)))
-extern void trace_performance_since(uint64_t start, const char *format, ...);
+void trace_performance_since(uint64_t start, const char *format, ...);
 
 __attribute__((format (printf, 1, 2)))
 void trace_performance_leave(const char *format, ...);
@@ -132,19 +132,19 @@
 
 /* backend functions, use non-*fl macros instead */
 __attribute__((format (printf, 4, 5)))
-extern void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
-				const char *format, ...);
+void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
+			 const char *format, ...);
 __attribute__((format (printf, 4, 5)))
-extern void trace_argv_printf_fl(const char *file, int line, const char **argv,
-				 const char *format, ...);
-extern void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
-			    const struct strbuf *data);
+void trace_argv_printf_fl(const char *file, int line, const char **argv,
+			  const char *format, ...);
+void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
+		     const struct strbuf *data);
 __attribute__((format (printf, 4, 5)))
-extern void trace_performance_fl(const char *file, int line,
-				 uint64_t nanos, const char *fmt, ...);
+void trace_performance_fl(const char *file, int line,
+			  uint64_t nanos, const char *fmt, ...);
 __attribute__((format (printf, 4, 5)))
-extern void trace_performance_leave_fl(const char *file, int line,
-				       uint64_t nanos, const char *fmt, ...);
+void trace_performance_leave_fl(const char *file, int line,
+				uint64_t nanos, const char *fmt, ...);
 static inline int trace_pass_fl(struct trace_key *key)
 {
 	return key->fd || !key->initialized;
diff --git a/trace2.c b/trace2.c
index 8bbad56..eb759f3 100644
--- a/trace2.c
+++ b/trace2.c
@@ -10,6 +10,7 @@
 #include "trace2/tr2_cmd_name.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
@@ -120,6 +121,7 @@
 	tr2_sid_release();
 	tr2_cmd_name_release();
 	tr2_cfg_free_patterns();
+	tr2_sysenv_release();
 
 	trace2_enabled = 0;
 }
@@ -142,6 +144,11 @@
 	raise(signo);
 }
 
+void trace2_initialize_clock(void)
+{
+	tr2tls_start_process_clock();
+}
+
 void trace2_initialize_fl(const char *file, int line)
 {
 	struct tr2_tgt *tgt_j;
@@ -150,6 +157,8 @@
 	if (trace2_enabled)
 		return;
 
+	tr2_sysenv_load();
+
 	if (!tr2_tgt_want_builtins())
 		return;
 	trace2_enabled = 1;
@@ -177,13 +186,19 @@
 {
 	struct tr2_tgt *tgt_j;
 	int j;
+	uint64_t us_now;
+	uint64_t us_elapsed_absolute;
 
 	if (!trace2_enabled)
 		return;
 
+	us_now = getnanotime() / 1000;
+	us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_start_fl)
-			tgt_j->pfn_start_fl(file, line, argv);
+			tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
+					    argv);
 }
 
 int trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -198,6 +213,8 @@
 	if (!trace2_enabled)
 		return code;
 
+	trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
+
 	tr2main_exit_code = code;
 
 	us_now = getnanotime() / 1000;
@@ -428,7 +445,7 @@
 	us_now = getnanotime() / 1000;
 	us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
-	tr2tls_create_self(thread_name);
+	tr2tls_create_self(thread_name, us_now);
 
 	for_each_wanted_builtin (j, tgt_j)
 		if (tgt_j->pfn_thread_start_fl)
diff --git a/trace2.h b/trace2.h
index b330a54..f189ef5 100644
--- a/trace2.h
+++ b/trace2.h
@@ -20,8 +20,26 @@
  */
 
 /*
+ * Initialize the TRACE2 clock and do nothing else, in particular
+ * no mallocs, no system inspection, and no environment inspection.
+ *
+ * This should be called at the very top of main() to capture the
+ * process start time.  This is intended to reduce chicken-n-egg
+ * bootstrap pressure.
+ *
+ * It is safe to call this more than once.  This allows capturing
+ * absolute startup costs on Windows which uses a little trickery
+ * to do setup work before common-main.c:main() is called.
+ *
+ * The main trace2_initialize_fl() may be called a little later
+ * after more infrastructure is established.
+ */
+void trace2_initialize_clock(void);
+
+/*
  * Initialize TRACE2 tracing facility if any of the builtin TRACE2
- * targets are enabled in the environment.  Emits a 'version' event.
+ * targets are enabled in the system config or the environment.
+ * Emits a 'version' event.
  *
  * Cleanup/Termination is handled automatically by a registered
  * atexit() routine.
@@ -108,10 +126,11 @@
  * Emit one or more 'def_param' events for "interesting" configuration
  * settings.
  *
- * The environment variable "GIT_TR2_CONFIG_PARAMS" can be set to a
- * list of patterns considered important.  For example:
- *
- *    GIT_TR2_CONFIG_PARAMS="core.*,remote.*.url"
+ * Use the TR2_SYSENV_CFG_PARAM setting to register a comma-separated
+ * list of patterns configured important.  For example:
+ *     git config --system trace2.configParams 'core.*,remote.*.url'
+ * or:
+ *     GIT_TR2_CONFIG_PARAMS=core.*,remote.*.url"
  *
  * Note: this routine does a read-only iteration on the config data
  * (using read_early_config()), so it must not be called until enough
@@ -372,13 +391,19 @@
  * Optional platform-specific code to dump information about the
  * current and any parent process(es).  This is intended to allow
  * post-processors to know who spawned this git instance and anything
- * else the platform may be able to tell us about the current process.
+ * else that the platform may be able to tell us about the current process.
  */
+
+enum trace2_process_info_reason {
+	TRACE2_PROCESS_INFO_STARTUP,
+	TRACE2_PROCESS_INFO_EXIT,
+};
+
 #if defined(GIT_WINDOWS_NATIVE)
-void trace2_collect_process_info(void);
+void trace2_collect_process_info(enum trace2_process_info_reason reason);
 #else
-#define trace2_collect_process_info() \
-	do {                          \
+#define trace2_collect_process_info(reason) \
+	do {                                \
 	} while (0)
 #endif
 
diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c
index b329921..caa7f06 100644
--- a/trace2/tr2_cfg.c
+++ b/trace2/tr2_cfg.c
@@ -1,8 +1,7 @@
 #include "cache.h"
 #include "config.h"
-#include "tr2_cfg.h"
-
-#define TR2_ENVVAR_CFG_PARAM "GIT_TR2_CONFIG_PARAMS"
+#include "trace2/tr2_cfg.h"
+#include "trace2/tr2_sysenv.h"
 
 static struct strbuf **tr2_cfg_patterns;
 static int tr2_cfg_count_patterns;
@@ -21,7 +20,7 @@
 		return tr2_cfg_count_patterns;
 	tr2_cfg_loaded = 1;
 
-	envvar = getenv(TR2_ENVVAR_CFG_PARAM);
+	envvar = tr2_sysenv_get(TR2_SYSENV_CFG_PARAM);
 	if (!envvar || !*envvar)
 		return tr2_cfg_count_patterns;
 
diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c
index fd490a4..5dda0ca 100644
--- a/trace2/tr2_dst.c
+++ b/trace2/tr2_dst.c
@@ -1,23 +1,19 @@
 #include "cache.h"
 #include "trace2/tr2_dst.h"
+#include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 
 /*
- * If a Trace2 target cannot be opened for writing, we should issue a
- * warning to stderr, but this is very annoying if the target is a pipe
- * or socket and beyond the user's control -- especially since every
- * git command (and sub-command) will print the message.  So we silently
- * eat these warnings and just discard the trace data.
- *
- * Enable the following environment variable to see these warnings.
+ * How many attempts we will make at creating an automatically-named trace file.
  */
-#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
+#define MAX_AUTO_ATTEMPTS 10
 
 static int tr2_dst_want_warning(void)
 {
 	static int tr2env_dst_debug = -1;
 
 	if (tr2env_dst_debug == -1) {
-		const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG);
+		const char *env_value = tr2_sysenv_get(TR2_SYSENV_DST_DEBUG);
 		if (!env_value || !*env_value)
 			tr2env_dst_debug = 0;
 		else
@@ -36,13 +32,65 @@
 	dst->need_close = 0;
 }
 
+static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix)
+{
+	int fd;
+	const char *last_slash, *sid = tr2_sid_get();
+	struct strbuf path = STRBUF_INIT;
+	size_t base_path_len;
+	unsigned attempt_count;
+
+	last_slash = strrchr(sid, '/');
+	if (last_slash)
+		sid = last_slash + 1;
+
+	strbuf_addstr(&path, tgt_prefix);
+	if (!is_dir_sep(path.buf[path.len - 1]))
+		strbuf_addch(&path, '/');
+	strbuf_addstr(&path, sid);
+	base_path_len = path.len;
+
+	for (attempt_count = 0; attempt_count < MAX_AUTO_ATTEMPTS; attempt_count++) {
+		if (attempt_count > 0) {
+			strbuf_setlen(&path, base_path_len);
+			strbuf_addf(&path, ".%d", attempt_count);
+		}
+
+		fd = open(path.buf, O_WRONLY | O_CREAT | O_EXCL, 0666);
+		if (fd != -1)
+			break;
+	}
+
+	if (fd == -1) {
+		if (tr2_dst_want_warning())
+			warning("trace2: could not open '%.*s' for '%s' tracing: %s",
+				(int) base_path_len, path.buf,
+				tr2_sysenv_display_name(dst->sysenv_var),
+				strerror(errno));
+
+		tr2_dst_trace_disable(dst);
+		strbuf_release(&path);
+		return 0;
+	}
+
+	strbuf_release(&path);
+
+	dst->fd = fd;
+	dst->need_close = 1;
+	dst->initialized = 1;
+
+	return dst->fd;
+}
+
 static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
 {
 	int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
 	if (fd == -1) {
 		if (tr2_dst_want_warning())
 			warning("trace2: could not open '%s' for '%s' tracing: %s",
-				tgt_value, dst->env_var_name, strerror(errno));
+				tgt_value,
+				tr2_sysenv_display_name(dst->sysenv_var),
+				strerror(errno));
 
 		tr2_dst_trace_disable(dst);
 		return 0;
@@ -116,7 +164,8 @@
 	if (!path || !*path) {
 		if (tr2_dst_want_warning())
 			warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing",
-				tgt_value, dst->env_var_name);
+				tgt_value,
+				tr2_sysenv_display_name(dst->sysenv_var));
 
 		tr2_dst_trace_disable(dst);
 		return 0;
@@ -126,7 +175,7 @@
 	    strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
 		if (tr2_dst_want_warning())
 			warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
-				path, dst->env_var_name);
+				path, tr2_sysenv_display_name(dst->sysenv_var));
 
 		tr2_dst_trace_disable(dst);
 		return 0;
@@ -148,7 +197,8 @@
 error:
 	if (tr2_dst_want_warning())
 		warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
-			path, dst->env_var_name, strerror(e));
+			path, tr2_sysenv_display_name(dst->sysenv_var),
+			strerror(e));
 
 	tr2_dst_trace_disable(dst);
 	return 0;
@@ -168,7 +218,7 @@
 	struct strbuf buf = STRBUF_INIT;
 
 	strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'",
-		    dst->env_var_name, tgt_value);
+		    tr2_sysenv_display_name(dst->sysenv_var), tgt_value);
 	warning("%s", buf.buf);
 
 	strbuf_release(&buf);
@@ -184,7 +234,7 @@
 
 	dst->initialized = 1;
 
-	tgt_value = getenv(dst->env_var_name);
+	tgt_value = tr2_sysenv_get(dst->sysenv_var);
 
 	if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
 	    !strcasecmp(tgt_value, "false")) {
@@ -202,8 +252,12 @@
 		return dst->fd;
 	}
 
-	if (is_absolute_path(tgt_value))
-		return tr2_dst_try_path(dst, tgt_value);
+	if (is_absolute_path(tgt_value)) {
+		if (is_directory(tgt_value))
+			return tr2_dst_try_auto_path(dst, tgt_value);
+		else
+			return tr2_dst_try_path(dst, tgt_value);
+	}
 
 #ifndef NO_UNIX_SOCKETS
 	if (starts_with(tgt_value, PREFIX_AF_UNIX))
@@ -246,7 +300,8 @@
 		return;
 
 	if (tr2_dst_want_warning())
-		warning("unable to write trace to '%s': %s", dst->env_var_name,
+		warning("unable to write trace to '%s': %s",
+			tr2_sysenv_display_name(dst->sysenv_var),
 			strerror(errno));
 	tr2_dst_trace_disable(dst);
 }
diff --git a/trace2/tr2_dst.h b/trace2/tr2_dst.h
index 9a64f05..3adf3ba 100644
--- a/trace2/tr2_dst.h
+++ b/trace2/tr2_dst.h
@@ -2,9 +2,10 @@
 #define TR2_DST_H
 
 struct strbuf;
+#include "trace2/tr2_sysenv.h"
 
 struct tr2_dst {
-	const char *const env_var_name;
+	enum tr2_sysenv_variable sysenv_var;
 	int fd;
 	unsigned int initialized : 1;
 	unsigned int need_close : 1;
diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c
index 984524a..5047095 100644
--- a/trace2/tr2_sid.c
+++ b/trace2/tr2_sid.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
 
 #define TR2_ENVVAR_PARENT_SID "GIT_TR2_PARENT_SID"
@@ -7,6 +8,53 @@
 static int tr2sid_nr_git_parents;
 
 /*
+ * Compute the final component of the SID representing the current process.
+ * This should uniquely identify the process and be a valid filename (to
+ * allow writing trace2 data to per-process files).  It should also be fixed
+ * length for possible use as a database key.
+ *
+ * "<yyyymmdd>T<hhmmss>.<fraction>Z-<host>-<process>"
+ *
+ * where <host> is a 9 character string:
+ *    "H<first_8_chars_of_sha1_of_hostname>"
+ *    "Localhost" when no hostname.
+ *
+ * where <process> is a 9 character string containing the least signifcant
+ * 32 bits in the process-id.
+ *    "P<pid>"
+ * (This is an abribrary choice.  On most systems pid_t is a 32 bit value,
+ * so limit doesn't matter.  On larger systems, a truncated value is fine
+ * for our purposes here.)
+ */
+static void tr2_sid_append_my_sid_component(void)
+{
+	const struct git_hash_algo *algo = &hash_algos[GIT_HASH_SHA1];
+	struct tr2_tbuf tb_now;
+	git_hash_ctx ctx;
+	pid_t pid = getpid();
+	unsigned char hash[GIT_MAX_RAWSZ + 1];
+	char hex[GIT_MAX_HEXSZ + 1];
+	char hostname[HOST_NAME_MAX + 1];
+
+	tr2_tbuf_utc_datetime(&tb_now);
+	strbuf_addstr(&tr2sid_buf, tb_now.buf);
+
+	strbuf_addch(&tr2sid_buf, '-');
+	if (xgethostname(hostname, sizeof(hostname)))
+		strbuf_add(&tr2sid_buf, "Localhost", 9);
+	else {
+		algo->init_fn(&ctx);
+		algo->update_fn(&ctx, hostname, strlen(hostname));
+		algo->final_fn(hash, &ctx);
+		hash_to_hex_algop_r(hex, hash, algo);
+		strbuf_addch(&tr2sid_buf, 'H');
+		strbuf_add(&tr2sid_buf, hex, 8);
+	}
+
+	strbuf_addf(&tr2sid_buf, "-P%08"PRIx32, (uint32_t)pid);
+}
+
+/*
  * Compute a "unique" session id (SID) for the current process.  This allows
  * all events from this process to have a single label (much like a PID).
  *
@@ -20,7 +68,6 @@
  */
 static void tr2_sid_compute(void)
 {
-	uint64_t us_now;
 	const char *parent_sid;
 
 	if (tr2sid_buf.len)
@@ -38,9 +85,7 @@
 		tr2sid_nr_git_parents++;
 	}
 
-	us_now = getnanotime() / 1000;
-	strbuf_addf(&tr2sid_buf, "%" PRIuMAX "-%" PRIdMAX, (uintmax_t)us_now,
-		    (intmax_t)getpid());
+	tr2_sid_append_my_sid_component();
 
 	setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1);
 }
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
new file mode 100644
index 0000000..9025b86
--- /dev/null
+++ b/trace2/tr2_sysenv.c
@@ -0,0 +1,127 @@
+#include "cache.h"
+#include "config.h"
+#include "dir.h"
+#include "tr2_sysenv.h"
+
+/*
+ * Each entry represents a trace2 setting.
+ * See Documentation/technical/api-trace2.txt
+ */
+struct tr2_sysenv_entry {
+	const char *env_var_name;
+	const char *git_config_name;
+
+	char *value;
+	unsigned int getenv_called : 1;
+};
+
+/*
+ * This table must match "enum tr2_sysenv_variable" in tr2_sysenv.h.
+ *
+ * The strings in this table are constant and must match the published
+ * config and environment variable names as described in the documentation.
+ *
+ * We do not define entries for the GIT_TR2_PARENT_* environment
+ * variables because they are transient and used to pass information
+ * from parent to child git processes, rather than settings.
+ */
+/* clang-format off */
+static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
+	[TR2_SYSENV_CFG_PARAM]     = { "GIT_TR2_CONFIG_PARAMS",
+				       "trace2.configparams" },
+
+	[TR2_SYSENV_DST_DEBUG]     = { "GIT_TR2_DST_DEBUG",
+				       "trace2.destinationdebug" },
+
+	[TR2_SYSENV_NORMAL]        = { "GIT_TR2",
+				       "trace2.normaltarget" },
+	[TR2_SYSENV_NORMAL_BRIEF]  = { "GIT_TR2_BRIEF",
+				       "trace2.normalbrief" },
+
+	[TR2_SYSENV_EVENT]         = { "GIT_TR2_EVENT",
+				       "trace2.eventtarget" },
+	[TR2_SYSENV_EVENT_BRIEF]   = { "GIT_TR2_EVENT_BRIEF",
+				       "trace2.eventbrief" },
+	[TR2_SYSENV_EVENT_NESTING] = { "GIT_TR2_EVENT_NESTING",
+				       "trace2.eventnesting" },
+
+	[TR2_SYSENV_PERF]          = { "GIT_TR2_PERF",
+				       "trace2.perftarget" },
+	[TR2_SYSENV_PERF_BRIEF]    = { "GIT_TR2_PERF_BRIEF",
+				       "trace2.perfbrief" },
+};
+/* clang-format on */
+
+static int tr2_sysenv_cb(const char *key, const char *value, void *d)
+{
+	int k;
+
+	if (!starts_with(key, "trace2."))
+		return 0;
+
+	for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
+		if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+			free(tr2_sysenv_settings[k].value);
+			tr2_sysenv_settings[k].value = xstrdup(value);
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Load Trace2 settings from the system config (usually "/etc/gitconfig"
+ * unless we were built with a runtime-prefix).  These are intended to
+ * define the default values for Trace2 as requested by the administrator.
+ *
+ * Then override with the Trace2 settings from the global config.
+ */
+void tr2_sysenv_load(void)
+{
+	if (ARRAY_SIZE(tr2_sysenv_settings) != TR2_SYSENV_MUST_BE_LAST)
+		BUG("tr2_sysenv_settings size is wrong");
+
+	read_very_early_config(tr2_sysenv_cb, NULL);
+}
+
+/*
+ * Return the value for the requested Trace2 setting from these sources:
+ * the system config, the global config, and the environment.
+ */
+const char *tr2_sysenv_get(enum tr2_sysenv_variable var)
+{
+	if (var >= TR2_SYSENV_MUST_BE_LAST)
+		BUG("tr2_sysenv_get invalid var '%d'", var);
+
+	if (!tr2_sysenv_settings[var].getenv_called) {
+		const char *v = getenv(tr2_sysenv_settings[var].env_var_name);
+		if (v && *v) {
+			free(tr2_sysenv_settings[var].value);
+			tr2_sysenv_settings[var].value = xstrdup(v);
+		}
+		tr2_sysenv_settings[var].getenv_called = 1;
+	}
+
+	return tr2_sysenv_settings[var].value;
+}
+
+/*
+ * Return a friendly name for this setting that is suitable for printing
+ * in an error messages.
+ */
+const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var)
+{
+	if (var >= TR2_SYSENV_MUST_BE_LAST)
+		BUG("tr2_sysenv_get invalid var '%d'", var);
+
+	return tr2_sysenv_settings[var].env_var_name;
+}
+
+void tr2_sysenv_release(void)
+{
+	int k;
+
+	for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++)
+		free(tr2_sysenv_settings[k].value);
+}
diff --git a/trace2/tr2_sysenv.h b/trace2/tr2_sysenv.h
new file mode 100644
index 0000000..369b20b
--- /dev/null
+++ b/trace2/tr2_sysenv.h
@@ -0,0 +1,36 @@
+#ifndef TR2_SYSENV_H
+#define TR2_SYSENV_H
+
+/*
+ * The Trace2 settings that can be loaded from /etc/gitconfig
+ * and/or user environment variables.
+ *
+ * Note that this set does not contain any of the transient
+ * environment variables used to pass information from parent
+ * to child git processes, such "GIT_TR2_PARENT_SID".
+ */
+enum tr2_sysenv_variable {
+	TR2_SYSENV_CFG_PARAM = 0,
+
+	TR2_SYSENV_DST_DEBUG,
+
+	TR2_SYSENV_NORMAL,
+	TR2_SYSENV_NORMAL_BRIEF,
+
+	TR2_SYSENV_EVENT,
+	TR2_SYSENV_EVENT_BRIEF,
+	TR2_SYSENV_EVENT_NESTING,
+
+	TR2_SYSENV_PERF,
+	TR2_SYSENV_PERF_BRIEF,
+
+	TR2_SYSENV_MUST_BE_LAST
+};
+
+void tr2_sysenv_load(void);
+
+const char *tr2_sysenv_get(enum tr2_sysenv_variable);
+const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var);
+void tr2_sysenv_release(void);
+
+#endif /* TR2_SYSENV_H */
diff --git a/trace2/tr2_tbuf.c b/trace2/tr2_tbuf.c
index 0844910..2498482 100644
--- a/trace2/tr2_tbuf.c
+++ b/trace2/tr2_tbuf.c
@@ -15,7 +15,7 @@
 		  tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
 }
 
-void tr2_tbuf_utc_time(struct tr2_tbuf *tb)
+void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb)
 {
 	struct timeval tv;
 	struct tm tm;
@@ -26,7 +26,22 @@
 	gmtime_r(&secs, &tm);
 
 	xsnprintf(tb->buf, sizeof(tb->buf),
-		  "%4d-%02d-%02d %02d:%02d:%02d.%06ld", tm.tm_year + 1900,
+		  "%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", tm.tm_year + 1900,
 		  tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
 		  (long)tv.tv_usec);
 }
+
+void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb)
+{
+	struct timeval tv;
+	struct tm tm;
+	time_t secs;
+
+	gettimeofday(&tv, NULL);
+	secs = tv.tv_sec;
+	gmtime_r(&secs, &tm);
+
+	xsnprintf(tb->buf, sizeof(tb->buf), "%4d%02d%02dT%02d%02d%02d.%06ldZ",
+		  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+		  tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
+}
diff --git a/trace2/tr2_tbuf.h b/trace2/tr2_tbuf.h
index 9cdefa3..fa853d8 100644
--- a/trace2/tr2_tbuf.h
+++ b/trace2/tr2_tbuf.h
@@ -16,8 +16,9 @@
 void tr2_tbuf_local_time(struct tr2_tbuf *tb);
 
 /*
- * Fill buffer with formatted UTC time string.
+ * Fill buffer with formatted UTC datatime string.
  */
-void tr2_tbuf_utc_time(struct tr2_tbuf *tb);
+void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb);
+void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb);
 
 #endif /* TR2_TBUF_H */
diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h
index 297bb8f..7b90469 100644
--- a/trace2/tr2_tgt.h
+++ b/trace2/tr2_tgt.h
@@ -15,6 +15,7 @@
 typedef void(tr2_tgt_evt_version_fl_t)(const char *file, int line);
 
 typedef void(tr2_tgt_evt_start_fl_t)(const char *file, int line,
+				     uint64_t us_elapsed_absolute,
 				     const char **argv);
 typedef void(tr2_tgt_evt_exit_fl_t)(const char *file, int line,
 				    uint64_t us_elapsed_absolute, int code);
diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c
index 1cf4f62..c2852d1 100644
--- a/trace2/tr2_tgt_event.c
+++ b/trace2/tr2_tgt_event.c
@@ -6,10 +6,11 @@
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 };
+static struct tr2_dst tr2dst_event = { TR2_SYSENV_EVENT, 0, 0, 0 };
 
 /*
  * The version number of the JSON data generated by the EVENT target
@@ -28,37 +29,36 @@
  * are primarily intended for the performance target during debugging.
  *
  * Some of the outer-most messages, however, may be of interest to the
- * event target.  Set this environment variable to a larger integer for
- * more detail in the event target.
+ * event target.  Use the TR2_SYSENV_EVENT_NESTING setting to increase
+ * region details in the event target.
  */
-#define TR2_ENVVAR_EVENT_NESTING "GIT_TR2_EVENT_NESTING"
-static int tr2env_event_nesting_wanted = 2;
+static int tr2env_event_max_nesting_levels = 2;
 
 /*
- * Set this environment variable to true to omit the <time>, <file>, and
+ * Use the TR2_SYSENV_EVENT_BRIEF to omit the <time>, <file>, and
  * <line> fields from most events.
  */
-#define TR2_ENVVAR_EVENT_BRIEF "GIT_TR2_EVENT_BRIEF"
-static int tr2env_event_brief;
+static int tr2env_event_be_brief;
 
 static int fn_init(void)
 {
 	int want = tr2_dst_trace_want(&tr2dst_event);
-	int want_nesting;
+	int max_nesting;
 	int want_brief;
-	char *nesting;
-	char *brief;
+	const char *nesting;
+	const char *brief;
 
 	if (!want)
 		return want;
 
-	nesting = getenv(TR2_ENVVAR_EVENT_NESTING);
-	if (nesting && ((want_nesting = atoi(nesting)) > 0))
-		tr2env_event_nesting_wanted = want_nesting;
+	nesting = tr2_sysenv_get(TR2_SYSENV_EVENT_NESTING);
+	if (nesting && *nesting && ((max_nesting = atoi(nesting)) > 0))
+		tr2env_event_max_nesting_levels = max_nesting;
 
-	brief = getenv(TR2_ENVVAR_EVENT_BRIEF);
-	if (brief && ((want_brief = atoi(brief)) > 0))
-		tr2env_event_brief = want_brief;
+	brief = tr2_sysenv_get(TR2_SYSENV_EVENT_BRIEF);
+	if (brief && *brief &&
+	    ((want_brief = git_parse_maybe_bool(brief)) != -1))
+		tr2env_event_be_brief = want_brief;
 
 	return want;
 }
@@ -92,13 +92,13 @@
 	/*
 	 * In brief mode, only emit <time> on these 2 event types.
 	 */
-	if (!tr2env_event_brief || !strcmp(event_name, "version") ||
+	if (!tr2env_event_be_brief || !strcmp(event_name, "version") ||
 	    !strcmp(event_name, "atexit")) {
-		tr2_tbuf_utc_time(&tb_now);
+		tr2_tbuf_utc_datetime_extended(&tb_now);
 		jw_object_string(jw, "time", tb_now.buf);
 	}
 
-	if (!tr2env_event_brief && file && *file) {
+	if (!tr2env_event_be_brief && file && *file) {
 		jw_object_string(jw, "file", file);
 		jw_object_intmax(jw, "line", line);
 	}
@@ -122,13 +122,16 @@
 	jw_release(&jw);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+			uint64_t us_elapsed_absolute, const char **argv)
 {
 	const char *event_name = "start";
 	struct json_writer jw = JSON_WRITER_INIT;
+	double t_abs = (double)us_elapsed_absolute / 1000000.0;
 
 	jw_object_begin(&jw, 0);
 	event_fmt_prepare(event_name, file, line, NULL, &jw);
+	jw_object_double(&jw, "t_abs", 6, t_abs);
 	jw_object_inline_begin_array(&jw, "argv");
 	jw_array_argv(&jw, argv);
 	jw_end(&jw);
@@ -456,7 +459,7 @@
 {
 	const char *event_name = "region_enter";
 	struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-	if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+	if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
 		struct json_writer jw = JSON_WRITER_INIT;
 
 		jw_object_begin(&jw, 0);
@@ -481,7 +484,7 @@
 {
 	const char *event_name = "region_leave";
 	struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-	if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+	if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
 		struct json_writer jw = JSON_WRITER_INIT;
 		double t_rel = (double)us_elapsed_region / 1000000.0;
 
@@ -508,7 +511,7 @@
 {
 	const char *event_name = "data";
 	struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-	if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+	if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
 		struct json_writer jw = JSON_WRITER_INIT;
 		double t_abs = (double)us_elapsed_absolute / 1000000.0;
 		double t_rel = (double)us_elapsed_region / 1000000.0;
@@ -536,7 +539,7 @@
 {
 	const char *event_name = "data_json";
 	struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
-	if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) {
+	if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) {
 		struct json_writer jw = JSON_WRITER_INIT;
 		double t_abs = (double)us_elapsed_absolute / 1000000.0;
 		double t_rel = (double)us_elapsed_region / 1000000.0;
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index 1a07d70..00b116d 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -4,20 +4,20 @@
 #include "quote.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_normal = { "GIT_TR2", 0, 0, 0 };
+static struct tr2_dst tr2dst_normal = { TR2_SYSENV_NORMAL, 0, 0, 0 };
 
 /*
- * Set this environment variable to true to omit the "<time> <file>:<line>"
+ * Use the TR2_SYSENV_NORMAL_BRIEF setting to omit the "<time> <file>:<line>"
  * fields from each line written to the builtin normal target.
  *
  * Unit tests may want to use this to help with testing.
  */
-#define TR2_ENVVAR_NORMAL_BRIEF "GIT_TR2_BRIEF"
-static int tr2env_normal_brief;
+static int tr2env_normal_be_brief;
 
 #define TR2FMT_NORMAL_FL_WIDTH (50)
 
@@ -25,15 +25,15 @@
 {
 	int want = tr2_dst_trace_want(&tr2dst_normal);
 	int want_brief;
-	char *brief;
+	const char *brief;
 
 	if (!want)
 		return want;
 
-	brief = getenv(TR2_ENVVAR_NORMAL_BRIEF);
+	brief = tr2_sysenv_get(TR2_SYSENV_NORMAL_BRIEF);
 	if (brief && *brief &&
 	    ((want_brief = git_parse_maybe_bool(brief)) != -1))
-		tr2env_normal_brief = want_brief;
+		tr2env_normal_be_brief = want_brief;
 
 	return want;
 }
@@ -47,7 +47,7 @@
 {
 	strbuf_setlen(buf, 0);
 
-	if (!tr2env_normal_brief) {
+	if (!tr2env_normal_be_brief) {
 		struct tr2_tbuf tb_now;
 
 		tr2_tbuf_local_time(&tb_now);
@@ -81,7 +81,8 @@
 	strbuf_release(&buf_payload);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+			uint64_t us_elapsed_absolute, const char **argv)
 {
 	struct strbuf buf_payload = STRBUF_INIT;
 
diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c
index 2a866d7..ea0cbbe 100644
--- a/trace2/tr2_tgt_perf.c
+++ b/trace2/tr2_tgt_perf.c
@@ -6,20 +6,20 @@
 #include "json-writer.h"
 #include "trace2/tr2_dst.h"
 #include "trace2/tr2_sid.h"
+#include "trace2/tr2_sysenv.h"
 #include "trace2/tr2_tbuf.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 
-static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 };
+static struct tr2_dst tr2dst_perf = { TR2_SYSENV_PERF, 0, 0, 0 };
 
 /*
- * Set this environment variable to true to omit the "<time> <file>:<line>"
+ * Use TR2_SYSENV_PERF_BRIEF to omit the "<time> <file>:<line>"
  * fields from each line written to the builtin performance target.
  *
  * Unit tests may want to use this to help with testing.
  */
-#define TR2_ENVVAR_PERF_BRIEF "GIT_TR2_PERF_BRIEF"
-static int tr2env_perf_brief;
+static int tr2env_perf_be_brief;
 
 #define TR2FMT_PERF_FL_WIDTH (50)
 #define TR2FMT_PERF_MAX_EVENT_NAME (12)
@@ -36,17 +36,17 @@
 {
 	int want = tr2_dst_trace_want(&tr2dst_perf);
 	int want_brief;
-	char *brief;
+	const char *brief;
 
 	if (!want)
 		return want;
 
 	strbuf_addchars(&dots, '.', TR2_DOTS_BUFFER_SIZE);
 
-	brief = getenv(TR2_ENVVAR_PERF_BRIEF);
+	brief = tr2_sysenv_get(TR2_SYSENV_PERF_BRIEF);
 	if (brief && *brief &&
 	    ((want_brief = git_parse_maybe_bool(brief)) != -1))
-		tr2env_perf_brief = want_brief;
+		tr2env_perf_be_brief = want_brief;
 
 	return want;
 }
@@ -77,7 +77,7 @@
 
 	strbuf_setlen(buf, 0);
 
-	if (!tr2env_perf_brief) {
+	if (!tr2env_perf_be_brief) {
 		struct tr2_tbuf tb_now;
 
 		tr2_tbuf_local_time(&tb_now);
@@ -159,15 +159,16 @@
 	strbuf_release(&buf_payload);
 }
 
-static void fn_start_fl(const char *file, int line, const char **argv)
+static void fn_start_fl(const char *file, int line,
+			uint64_t us_elapsed_absolute, const char **argv)
 {
 	const char *event_name = "start";
 	struct strbuf buf_payload = STRBUF_INIT;
 
 	sq_quote_argv_pretty(&buf_payload, argv);
 
-	perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
-			 &buf_payload);
+	perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
+			 NULL, NULL, &buf_payload);
 	strbuf_release(&buf_payload);
 }
 
diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c
index 8e65b03..e76d8c5 100644
--- a/trace2/tr2_tls.c
+++ b/trace2/tr2_tls.c
@@ -10,16 +10,30 @@
 #define TR2_REGION_NESTING_INITIAL_SIZE (100)
 
 static struct tr2tls_thread_ctx *tr2tls_thread_main;
-static uint64_t tr2tls_us_start_main;
+static uint64_t tr2tls_us_start_process;
 
 static pthread_mutex_t tr2tls_mutex;
 static pthread_key_t tr2tls_key;
 
 static int tr2_next_thread_id; /* modify under lock */
 
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name)
+void tr2tls_start_process_clock(void)
 {
-	uint64_t us_now = getnanotime() / 1000;
+	if (tr2tls_us_start_process)
+		return;
+
+	/*
+	 * Keep the absolute start time of the process (i.e. the main
+	 * process) in a fixed variable since other threads need to
+	 * access it.  This allows them to do that without a lock on
+	 * main thread's array data (because of reallocs).
+	 */
+	tr2tls_us_start_process = getnanotime() / 1000;
+}
+
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+					     uint64_t us_thread_start)
+{
 	struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx));
 
 	/*
@@ -29,7 +43,7 @@
 	 */
 	ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE;
 	ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t));
-	ctx->array_us_start[ctx->nr_open_regions++] = us_now;
+	ctx->array_us_start[ctx->nr_open_regions++] = us_thread_start;
 
 	ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id);
 
@@ -55,7 +69,7 @@
 	 * here and silently continue.
 	 */
 	if (!ctx)
-		ctx = tr2tls_create_self("unknown");
+		ctx = tr2tls_create_self("unknown", getnanotime() / 1000);
 
 	return ctx;
 }
@@ -124,22 +138,18 @@
 	if (!tr2tls_thread_main)
 		return 0;
 
-	return us - tr2tls_us_start_main;
+	return us - tr2tls_us_start_process;
 }
 
 void tr2tls_init(void)
 {
+	tr2tls_start_process_clock();
+
 	pthread_key_create(&tr2tls_key, NULL);
 	init_recursive_mutex(&tr2tls_mutex);
 
-	tr2tls_thread_main = tr2tls_create_self("main");
-	/*
-	 * Keep a copy of the absolute start time of the main thread
-	 * in a fixed variable since other threads need to access it.
-	 * This also eliminates the need to lock accesses to the main
-	 * thread's array (because of reallocs).
-	 */
-	tr2tls_us_start_main = tr2tls_thread_main->array_us_start[0];
+	tr2tls_thread_main =
+		tr2tls_create_self("main", tr2tls_us_start_process);
 }
 
 void tr2tls_release(void)
diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h
index bb80e3f..b1e327a 100644
--- a/trace2/tr2_tls.h
+++ b/trace2/tr2_tls.h
@@ -31,7 +31,8 @@
  * In this and all following functions the term "self" refers to the
  * current thread.
  */
-struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name);
+struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name,
+					     uint64_t us_thread_start);
 
 /*
  * Get our TLS data.
@@ -94,4 +95,9 @@
  */
 int tr2tls_locked_increment(int *p);
 
+/*
+ * Capture the process start time and do nothing else.
+ */
+void tr2tls_start_process_clock(void);
+
 #endif /* TR2_TLS_H */
diff --git a/transport.c b/transport.c
index d0608df..f1fcd2c 100644
--- a/transport.c
+++ b/transport.c
@@ -252,6 +252,14 @@
 	return 0;
 }
 
+static void die_if_server_options(struct transport *transport)
+{
+	if (!transport->server_options || !transport->server_options->nr)
+		return;
+	advise(_("see protocol.version in 'git help config' for more details"));
+	die(_("server options require protocol version 2 or later"));
+}
+
 /*
  * Obtains the protocol version from the transport and writes it to
  * transport->data->version, first connecting if not already connected.
@@ -286,6 +294,7 @@
 		break;
 	case protocol_v1:
 	case protocol_v0:
+		die_if_server_options(transport);
 		get_remote_heads(&reader, &refs,
 				 for_push ? REF_NORMAL : 0,
 				 &data->extra_have,
@@ -314,7 +323,6 @@
 	int ret = 0;
 	struct git_transport_data *data = transport->data;
 	struct ref *refs = NULL;
-	char *dest = xstrdup(transport->url);
 	struct fetch_pack_args args;
 	struct ref *refs_tmp = NULL;
 
@@ -356,16 +364,17 @@
 
 	switch (data->version) {
 	case protocol_v2:
-		refs = fetch_pack(&args, data->fd, data->conn,
+		refs = fetch_pack(&args, data->fd,
 				  refs_tmp ? refs_tmp : transport->remote_refs,
-				  dest, to_fetch, nr_heads, &data->shallow,
+				  to_fetch, nr_heads, &data->shallow,
 				  &transport->pack_lockfile, data->version);
 		break;
 	case protocol_v1:
 	case protocol_v0:
-		refs = fetch_pack(&args, data->fd, data->conn,
+		die_if_server_options(transport);
+		refs = fetch_pack(&args, data->fd,
 				  refs_tmp ? refs_tmp : transport->remote_refs,
-				  dest, to_fetch, nr_heads, &data->shallow,
+				  to_fetch, nr_heads, &data->shallow,
 				  &transport->pack_lockfile, data->version);
 		break;
 	case protocol_unknown_version:
@@ -389,7 +398,6 @@
 
 	free_refs(refs_tmp);
 	free_refs(refs);
-	free(dest);
 	return ret;
 }
 
diff --git a/transport.h b/transport.h
index f2ee7c4..06e06d3 100644
--- a/transport.h
+++ b/transport.h
@@ -134,7 +134,7 @@
 #define TRANSPORT_PUSH_OPTIONS			(1<<14)
 #define TRANSPORT_RECURSE_SUBMODULES_ONLY	(1<<15)
 
-extern int transport_summary_width(const struct ref *refs);
+int transport_summary_width(const struct ref *refs);
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
@@ -263,5 +263,5 @@
 		  int verbose, int porcelain, unsigned int *reject_reasons);
 
 typedef void alternate_ref_fn(const struct object_id *oid, void *);
-extern void for_each_alternate_ref(alternate_ref_fn, void *);
+void for_each_alternate_ref(alternate_ref_fn, void *);
 #endif
diff --git a/tree-diff.c b/tree-diff.c
index e6d306f..f1f641e 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -181,7 +181,7 @@
 	struct tree_desc *t, struct tree_desc *tp,
 	int imin)
 {
-	unsigned mode;
+	unsigned short mode;
 	const char *path;
 	const struct object_id *oid;
 	int pathlen;
diff --git a/tree-walk.c b/tree-walk.c
index 1e4bbc8..ec32a47 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -500,7 +500,7 @@
 	struct object_id oid;
 };
 
-static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned *mode)
+static int find_tree_entry(struct tree_desc *t, const char *name, struct object_id *result, unsigned short *mode)
 {
 	int namelen = strlen(name);
 	while (t->size) {
@@ -535,7 +535,7 @@
 	return -1;
 }
 
-int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned *mode)
+int get_tree_entry(const struct object_id *tree_oid, const char *name, struct object_id *oid, unsigned short *mode)
 {
 	int retval;
 	void *tree;
@@ -585,7 +585,7 @@
  * See the code for enum get_oid_result for a description of
  * the return values.
  */
-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode)
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode)
 {
 	int retval = MISSING_OBJECT;
 	struct dir_state *parents = NULL;
diff --git a/tree-walk.h b/tree-walk.h
index 8225171..161e240 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -16,7 +16,7 @@
 	unsigned int size;
 };
 
-static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
+static inline const struct object_id *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned short *modep)
 {
 	*pathp = desc->entry.path;
 	*modep = desc->entry.mode;
@@ -51,7 +51,7 @@
 typedef int (*traverse_callback_t)(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *);
 int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info);
 
-enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned *mode);
+enum get_oid_result get_tree_entry_follow_symlinks(struct object_id *tree_oid, const char *name, struct object_id *result, struct strbuf *result_path, unsigned short *mode);
 
 struct traverse_info {
 	const char *traverse_path;
@@ -66,9 +66,9 @@
 	int show_all_errors;
 };
 
-int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned *);
-extern char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
-extern void setup_traverse_info(struct traverse_info *info, const char *base);
+int get_tree_entry(const struct object_id *, const char *, struct object_id *, unsigned short *);
+char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n);
+void setup_traverse_info(struct traverse_info *info, const char *base);
 
 static inline int traverse_path_len(const struct traverse_info *info, const struct name_entry *n)
 {
diff --git a/unpack-trees.c b/unpack-trees.c
index 1ccd343..5018990 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -219,6 +219,9 @@
 			     enum unpack_trees_error_types e,
 			     const char *path)
 {
+	if (o->quiet)
+		return -1;
+
 	if (!o->show_all_errors)
 		return error(ERRORMSG(o, e), super_prefixed(path));
 
@@ -268,8 +271,7 @@
 		flags |= SUBMODULE_MOVE_HEAD_FORCE;
 
 	if (submodule_move_head(ce->name, old_id, new_id, flags))
-		return o->gently ? -1 :
-				   add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
+		return add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
 	return 0;
 }
 
@@ -404,20 +406,21 @@
 		 * below.
 		 */
 		struct oid_array to_fetch = OID_ARRAY_INIT;
-		int fetch_if_missing_store = fetch_if_missing;
-		fetch_if_missing = 0;
 		for (i = 0; i < index->cache_nr; i++) {
 			struct cache_entry *ce = index->cache[i];
-			if ((ce->ce_flags & CE_UPDATE) &&
-			    !S_ISGITLINK(ce->ce_mode)) {
-				if (!has_object_file(&ce->oid))
-					oid_array_append(&to_fetch, &ce->oid);
-			}
+
+			if (!(ce->ce_flags & CE_UPDATE) ||
+			    S_ISGITLINK(ce->ce_mode))
+				continue;
+			if (!oid_object_info_extended(the_repository, &ce->oid,
+						      NULL,
+						      OBJECT_INFO_FOR_PREFETCH))
+				continue;
+			oid_array_append(&to_fetch, &ce->oid);
 		}
 		if (to_fetch.nr)
 			fetch_objects(repository_format_partial_clone,
 				      to_fetch.oid, to_fetch.nr);
-		fetch_if_missing = fetch_if_missing_store;
 		oid_array_clear(&to_fetch);
 	}
 	for (i = 0; i < index->cache_nr; i++) {
@@ -707,7 +710,6 @@
  * instead of ODB since we already know what these trees contain.
  */
 static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names,
-				  struct name_entry *names,
 				  struct traverse_info *info)
 {
 	struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -797,7 +799,7 @@
 		 * unprocessed entries before 'pos'.
 		 */
 		bottom = o->cache_bottom;
-		ret = traverse_by_cache_tree(pos, nr_entries, n, names, info);
+		ret = traverse_by_cache_tree(pos, nr_entries, n, info);
 		o->cache_bottom = bottom;
 		return ret;
 	}
@@ -1040,7 +1042,7 @@
 static int unpack_failed(struct unpack_trees_options *o, const char *message)
 {
 	discard_index(&o->result);
-	if (!o->gently && !o->exiting_early) {
+	if (!o->quiet && !o->exiting_early) {
 		if (message)
 			return error("%s", message);
 		return -1;
@@ -1618,6 +1620,8 @@
 						  WRITE_TREE_SILENT |
 						  WRITE_TREE_REPAIR);
 		}
+
+		o->result.updated_workdir = 1;
 		discard_index(o->dst_index);
 		*o->dst_index = o->result;
 	} else {
@@ -1645,8 +1649,7 @@
 static int reject_merge(const struct cache_entry *ce,
 			struct unpack_trees_options *o)
 {
-	return o->gently ? -1 :
-		add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
+	return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
 static int same(const struct cache_entry *a, const struct cache_entry *b)
@@ -1693,8 +1696,7 @@
 			int r = check_submodule_move_head(ce,
 				"HEAD", oid_to_hex(&ce->oid), o);
 			if (r)
-				return o->gently ? -1 :
-					add_rejected_path(o, error_type, ce->name);
+				return add_rejected_path(o, error_type, ce->name);
 			return 0;
 		}
 
@@ -1712,8 +1714,7 @@
 	}
 	if (errno == ENOENT)
 		return 0;
-	return o->gently ? -1 :
-		add_rejected_path(o, error_type, ce->name);
+	return add_rejected_path(o, error_type, ce->name);
 }
 
 int verify_uptodate(const struct cache_entry *ce,
@@ -1759,7 +1760,6 @@
  */
 static int verify_clean_submodule(const char *old_sha1,
 				  const struct cache_entry *ce,
-				  enum unpack_trees_error_types error_type,
 				  struct unpack_trees_options *o)
 {
 	if (!submodule_from_ce(ce))
@@ -1770,7 +1770,6 @@
 }
 
 static int verify_clean_subdirectory(const struct cache_entry *ce,
-				     enum unpack_trees_error_types error_type,
 				     struct unpack_trees_options *o)
 {
 	/*
@@ -1793,7 +1792,7 @@
 		if (!sub_head && oideq(&oid, &ce->oid))
 			return 0;
 		return verify_clean_submodule(sub_head ? NULL : oid_to_hex(&oid),
-					      ce, error_type, o);
+					      ce, o);
 	}
 
 	/*
@@ -1835,8 +1834,7 @@
 		d.exclude_per_dir = o->dir->exclude_per_dir;
 	i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
 	if (i)
-		return o->gently ? -1 :
-			add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
+		return add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
 	free(pathbuf);
 	return cnt;
 }
@@ -1889,7 +1887,7 @@
 		 * files that are in "foo/" we would lose
 		 * them.
 		 */
-		if (verify_clean_subdirectory(ce, error_type, o) < 0)
+		if (verify_clean_subdirectory(ce, o) < 0)
 			return -1;
 		return 0;
 	}
@@ -1905,8 +1903,7 @@
 			return 0;
 	}
 
-	return o->gently ? -1 :
-		add_rejected_path(o, error_type, name);
+	return add_rejected_path(o, error_type, name);
 }
 
 /*
@@ -2346,7 +2343,7 @@
 		return error("Cannot do a bind merge of %d trees",
 			     o->merge_size);
 	if (a && old)
-		return o->gently ? -1 :
+		return o->quiet ? -1 :
 			error(ERRORMSG(o, ERROR_BIND_OVERLAP),
 			      super_prefixed(a->name),
 			      super_prefixed(old->name));
diff --git a/unpack-trees.h b/unpack-trees.h
index 0135080..d344d7d 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -56,7 +56,7 @@
 		     diff_index_cached,
 		     debug_unpack,
 		     skip_sparse_checkout,
-		     gently,
+		     quiet,
 		     exiting_early,
 		     show_all_errors,
 		     dry_run;
diff --git a/upload-pack.c b/upload-pack.c
index d098ef5..d2ea5eb 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -592,7 +592,8 @@
 	return 1;
 }
 
-static void check_non_tip(struct object_array *want_obj)
+static void check_non_tip(struct object_array *want_obj,
+			  struct packet_writer *writer)
 {
 	int i;
 
@@ -611,9 +612,13 @@
 	/* Pick one of them (we know there at least is one) */
 	for (i = 0; i < want_obj->nr; i++) {
 		struct object *o = want_obj->objects[i].item;
-		if (!is_our_ref(o))
+		if (!is_our_ref(o)) {
+			packet_writer_error(writer,
+					    "upload-pack: not our ref %s",
+					    oid_to_hex(&o->oid));
 			die("git upload-pack: not our ref %s",
 			    oid_to_hex(&o->oid));
+		}
 	}
 }
 
@@ -834,7 +839,7 @@
 	if (skip_prefix(line, "deepen-not ", &arg)) {
 		char *ref = NULL;
 		struct object_id oid;
-		if (expand_ref(arg, strlen(arg), &oid, &ref) != 1)
+		if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
 			die("git upload-pack: ambiguous deepen-not: %s", line);
 		string_list_append(deepen_not, ref);
 		free(ref);
@@ -936,7 +941,7 @@
 	 * by another process that handled the initial request.
 	 */
 	if (has_non_tip)
-		check_non_tip(want_obj);
+		check_non_tip(want_obj, &writer);
 
 	if (!use_sideband && daemon_mode)
 		no_progress = 1;
diff --git a/upload-pack.h b/upload-pack.h
index cab2178..4bafe16 100644
--- a/upload-pack.h
+++ b/upload-pack.h
@@ -13,11 +13,11 @@
 struct repository;
 struct argv_array;
 struct packet_reader;
-extern int upload_pack_v2(struct repository *r, struct argv_array *keys,
-			  struct packet_reader *request);
+int upload_pack_v2(struct repository *r, struct argv_array *keys,
+		   struct packet_reader *request);
 
 struct strbuf;
-extern int upload_pack_advertise(struct repository *r,
-				 struct strbuf *value);
+int upload_pack_advertise(struct repository *r,
+			  struct strbuf *value);
 
 #endif /* UPLOAD_PACK_H */
diff --git a/url.h b/url.h
index f311c40..00b7d58 100644
--- a/url.h
+++ b/url.h
@@ -3,14 +3,14 @@
 
 struct strbuf;
 
-extern int is_url(const char *url);
-extern int is_urlschemechar(int first_flag, int ch);
-extern char *url_decode(const char *url);
-extern char *url_decode_mem(const char *url, int len);
-extern char *url_decode_parameter_name(const char **query);
-extern char *url_decode_parameter_value(const char **query);
+int is_url(const char *url);
+int is_urlschemechar(int first_flag, int ch);
+char *url_decode(const char *url);
+char *url_decode_mem(const char *url, int len);
+char *url_decode_parameter_name(const char **query);
+char *url_decode_parameter_value(const char **query);
 
-extern void end_url_with_slash(struct strbuf *buf, const char *url);
-extern void str_end_url_with_slash(const char *url, char **dest);
+void end_url_with_slash(struct strbuf *buf, const char *url);
+void str_end_url_with_slash(const char *url, char **dest);
 
 #endif /* URL_H */
diff --git a/urlmatch.h b/urlmatch.h
index e482148..eed5f29 100644
--- a/urlmatch.h
+++ b/urlmatch.h
@@ -33,7 +33,7 @@
 				 * '?...' and '#...' portion; will always be >= 1 */
 };
 
-extern char *url_normalize(const char *, struct url_info *);
+char *url_normalize(const char *, struct url_info *);
 
 struct urlmatch_item {
 	size_t hostmatch_len;
@@ -52,6 +52,6 @@
 	int (*cascade_fn)(const char *var, const char *value, void *cb);
 };
 
-extern int urlmatch_config_entry(const char *var, const char *value, void *cb);
+int urlmatch_config_entry(const char *var, const char *value, void *cb);
 
 #endif /* URL_MATCH_H */
diff --git a/utf8.h b/utf8.h
index 84efbfc..fcd5167 100644
--- a/utf8.h
+++ b/utf8.h
@@ -16,7 +16,7 @@
 int utf8_fprintf(FILE *, const char *, ...);
 
 extern const char utf8_bom[];
-extern int skip_utf8_bom(char **, size_t);
+int skip_utf8_bom(char **, size_t);
 
 void strbuf_add_wrapped_text(struct strbuf *buf,
 		const char *text, int indent, int indent2, int width);
diff --git a/varint.h b/varint.h
index c1c44d9..f78bb0c 100644
--- a/varint.h
+++ b/varint.h
@@ -1,7 +1,7 @@
 #ifndef VARINT_H
 #define VARINT_H
 
-extern int encode_varint(uintmax_t, unsigned char *);
-extern uintmax_t decode_varint(const unsigned char **);
+int encode_varint(uintmax_t, unsigned char *);
+uintmax_t decode_varint(const unsigned char **);
 
 #endif /* VARINT_H */
diff --git a/vcs-svn/sliding_window.h b/vcs-svn/sliding_window.h
index 189c32d..a7fc099 100644
--- a/vcs-svn/sliding_window.h
+++ b/vcs-svn/sliding_window.h
@@ -13,6 +13,6 @@
 
 #define SLIDING_VIEW_INIT(input, len)	{ (input), 0, 0, (len), STRBUF_INIT }
 
-extern int move_window(struct sliding_view *view, off_t off, size_t width);
+int move_window(struct sliding_view *view, off_t off, size_t width);
 
 #endif
diff --git a/vcs-svn/svndiff.h b/vcs-svn/svndiff.h
index 10a2cbc..625d950 100644
--- a/vcs-svn/svndiff.h
+++ b/vcs-svn/svndiff.h
@@ -4,7 +4,7 @@
 struct line_buffer;
 struct sliding_view;
 
-extern int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
-		struct sliding_view *preimage, FILE *postimage);
+int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
+		   struct sliding_view *preimage, FILE *postimage);
 
 #endif
diff --git a/worktree.c b/worktree.c
index b45bfeb9..4f66cd9 100644
--- a/worktree.c
+++ b/worktree.c
@@ -49,18 +49,24 @@
 	struct worktree *worktree = NULL;
 	struct strbuf path = STRBUF_INIT;
 	struct strbuf worktree_path = STRBUF_INIT;
-	int is_bare = 0;
 
 	strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
-	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
-	if (is_bare)
+	if (!strbuf_strip_suffix(&worktree_path, "/.git"))
 		strbuf_strip_suffix(&worktree_path, "/.");
 
 	strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
 
 	worktree = xcalloc(1, sizeof(*worktree));
 	worktree->path = strbuf_detach(&worktree_path, NULL);
-	worktree->is_bare = is_bare;
+	/*
+	 * NEEDSWORK: If this function is called from a secondary worktree and
+	 * config.worktree is present, is_bare_repository_cfg will reflect the
+	 * contents of config.worktree, not the contents of the main worktree.
+	 * This means that worktree->is_bare may be set to 0 even if the main
+	 * worktree is configured to be bare.
+	 */
+	worktree->is_bare = (is_bare_repository_cfg == 1) ||
+		is_bare_repository();
 	add_head_info(worktree);
 
 	strbuf_release(&path);
diff --git a/worktree.h b/worktree.h
index 9e3b0b7..caecc7a 100644
--- a/worktree.h
+++ b/worktree.h
@@ -30,37 +30,37 @@
  * The caller is responsible for freeing the memory from the returned
  * worktree(s).
  */
-extern struct worktree **get_worktrees(unsigned flags);
+struct worktree **get_worktrees(unsigned flags);
 
 /*
  * Returns 1 if linked worktrees exist, 0 otherwise.
  */
-extern int submodule_uses_worktrees(const char *path);
+int submodule_uses_worktrees(const char *path);
 
 /*
  * Return git dir of the worktree. Note that the path may be relative.
  * If wt is NULL, git dir of current worktree is returned.
  */
-extern const char *get_worktree_git_dir(const struct worktree *wt);
+const char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
  * Search a worktree that can be unambiguously identified by
  * "arg". "prefix" must not be NULL.
  */
-extern struct worktree *find_worktree(struct worktree **list,
-				      const char *prefix,
-				      const char *arg);
+struct worktree *find_worktree(struct worktree **list,
+			       const char *prefix,
+			       const char *arg);
 
 /*
  * Return true if the given worktree is the main one.
  */
-extern int is_main_worktree(const struct worktree *wt);
+int is_main_worktree(const struct worktree *wt);
 
 /*
  * Return the reason string if the given worktree is locked or NULL
  * otherwise.
  */
-extern const char *worktree_lock_reason(struct worktree *wt);
+const char *worktree_lock_reason(struct worktree *wt);
 
 #define WT_VALIDATE_WORKTREE_MISSING_OK (1 << 0)
 
@@ -68,28 +68,28 @@
  * Return zero if the worktree is in good condition. Error message is
  * returned if "errmsg" is not NULL.
  */
-extern int validate_worktree(const struct worktree *wt,
-			     struct strbuf *errmsg,
-			     unsigned flags);
+int validate_worktree(const struct worktree *wt,
+		      struct strbuf *errmsg,
+		      unsigned flags);
 
 /*
  * Update worktrees/xxx/gitdir with the new path.
  */
-extern void update_worktree_location(struct worktree *wt,
-				     const char *path_);
+void update_worktree_location(struct worktree *wt,
+			      const char *path_);
 
 /*
  * Free up the memory for worktree(s)
  */
-extern void free_worktrees(struct worktree **);
+void free_worktrees(struct worktree **);
 
 /*
  * Check if a per-worktree symref points to a ref in the main worktree
  * or any linked worktree, and return the worktree that holds the ref,
  * or NULL otherwise. The result may be destroyed by the next call.
  */
-extern const struct worktree *find_shared_symref(const char *symref,
-						 const char *target);
+const struct worktree *find_shared_symref(const char *symref,
+					  const char *target);
 
 /*
  * Similar to head_ref() for all HEADs _except_ one from the current
@@ -104,8 +104,8 @@
  * Similar to git_path() but can produce paths for a specified
  * worktree instead of current one
  */
-extern const char *worktree_git_path(const struct worktree *wt,
-				     const char *fmt, ...)
+const char *worktree_git_path(const struct worktree *wt,
+			      const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
diff --git a/wt-status.c b/wt-status.c
index 445a362..f4fa982 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1006,13 +1006,19 @@
 	return len;
 }
 
-void wt_status_add_cut_line(FILE *fp)
+void wt_status_append_cut_line(struct strbuf *buf)
 {
 	const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
+
+	strbuf_commented_addf(buf, "%s", cut_line);
+	strbuf_add_commented_lines(buf, explanation, strlen(explanation));
+}
+
+void wt_status_add_cut_line(FILE *fp)
+{
 	struct strbuf buf = STRBUF_INIT;
 
-	fprintf(fp, "%c %s", comment_line_char, cut_line);
-	strbuf_add_commented_lines(&buf, explanation, strlen(explanation));
+	wt_status_append_cut_line(&buf);
 	fputs(buf.buf, fp);
 	strbuf_release(&buf);
 }
@@ -1851,7 +1857,7 @@
 	color_fprintf(s->fp, branch_color_local, "%s", branch_name);
 
 	sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,
-				 s->ahead_behind_flags);
+				 0, s->ahead_behind_flags);
 	if (sti < 0) {
 		if (!base)
 			goto conclude;
@@ -1990,7 +1996,7 @@
 		branch = branch_get(branch_name);
 		base = NULL;
 		ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
-					     &base, s->ahead_behind_flags);
+					     &base, 0, s->ahead_behind_flags);
 		if (base) {
 			base = shorten_unambiguous_ref(base, 0);
 			fprintf(s->fp, "# branch.upstream %s%c", base, eol);
diff --git a/wt-status.h b/wt-status.h
index 3a95975..64f1ddc 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -129,6 +129,7 @@
 };
 
 size_t wt_status_locate_end(const char *s, size_t len);
+void wt_status_append_cut_line(struct strbuf *buf);
 void wt_status_add_cut_line(FILE *fp);
 void wt_status_prepare(struct repository *r, struct wt_status *s);
 void wt_status_print(struct wt_status *s);
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 2d41fff..ede4246 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -27,9 +27,9 @@
 void read_mmblob(mmfile_t *ptr, const struct object_id *oid);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
-extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
-extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
-extern int git_xmerge_config(const char *var, const char *value, void *cb);
+void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+void xdiff_clear_find_func(xdemitconf_t *xecfg);
+int git_xmerge_config(const char *var, const char *value, void *cb);
 extern int git_xmerge_style;
 
 /*
@@ -46,14 +46,14 @@
  * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
  * are treated for the comparision.
  */
-extern int xdiff_compare_lines(const char *l1, long s1,
-			       const char *l2, long s2, long flags);
+int xdiff_compare_lines(const char *l1, long s1,
+			const char *l2, long s2, long flags);
 
 /*
  * Returns a hash of the string s of length len.
  * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
  * are treated for the hash.
  */
-extern unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
+unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
 
 #endif
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index b158369..032e3a9 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -113,9 +113,9 @@
 } bdiffparam_t;
 
 
-#define xdl_malloc(x) malloc(x)
+#define xdl_malloc(x) xmalloc(x)
 #define xdl_free(ptr) free(ptr)
-#define xdl_realloc(ptr,x) realloc(ptr,x)
+#define xdl_realloc(ptr,x) xrealloc(ptr,x)
 
 void *xdl_mmfile_first(mmfile_t *mmf, long *size);
 long xdl_mmfile_size(mmfile_t *mmf);
diff --git a/xdiff/xinclude.h b/xdiff/xinclude.h
index f35c448..a4285ac 100644
--- a/xdiff/xinclude.h
+++ b/xdiff/xinclude.h
@@ -23,13 +23,7 @@
 #if !defined(XINCLUDE_H)
 #define XINCLUDE_H
 
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <limits.h>
-
+#include "git-compat-util.h"
 #include "xmacros.h"
 #include "xdiff.h"
 #include "xtypes.h"