Merge branch 'nd/parse-options-aliases'

Attempt to use an abbreviated option in "git clone --recurs" is
responded by a request to disambiguate between --recursive and
--recurse-submodules, which is bad because these two are synonyms.
The parse-options API has been extended to define such synonyms
more easily and not produce an unnecessary failure.

* nd/parse-options-aliases:
  parse-options: don't emit "ambiguous option" for aliases
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 85e1122..114f147 100644
--- a/Documentation/RelNotes/2.22.0.txt
+++ b/Documentation/RelNotes/2.22.0.txt
@@ -67,6 +67,38 @@
  * "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.
+
+ * The connectivity bitmaps are created by default in bare
+   repositories now; also the pathname hash-cache is created by
+   default to avoid making crappy deltas when repacking.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -130,6 +162,22 @@
    achieve better performance by batching the request for these
    promised blobs.
 
+ * During an initial "git clone --depth=..." partial clone, it is
+   pointless to spend cycles for a large portion of the connectivity
+   check that enumerates and skips promisor objects (which by
+   definition is all objects fetched from the other side).  This has
+   been optimized out.
+
+ * Mechanically and systematically drop "extern" from function
+   declarlation.
+
+ * The script to aggregate perf result unconditionally depended on
+   libjson-perl even though it did not have to, which has been
+   corrected.
+
+ * The internal implementation of "git rebase -i" has been updated to
+   avoid forking a separate "rebase--interactive" process.
+
 
 Fixes since v2.21
 -----------------
@@ -347,10 +395,85 @@
    eager and considered nonsense strings as if they can be legitimate
    beginning of *-by: trailer.  This has been tightened.
 
- * Build with gettext breaks on recent macOS w/ Homebrew when
-   /usr/local/bin is not on PATH, which has been corrected.
+ * 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).
+
+ * Fix index-pack perf test so that the repeated invocations always
+   run in an empty repository, which emulates the initial clone
+   situation better.
+   (merge 775c71e16d jk/p5302-avoid-collision-check-cost later to maint).
+
+ * A "ls-files" that emulates "find" to enumerate files in the working
+   tree resulted in duplicated Makefile rules that caused the build to
+   issue an unnecessary warning during a trial build after merge
+   conflicts are resolved in working tree *.h files but before the
+   resolved results are added to the index.  This has been corrected.
+
+ * "git chery-pick" (and "revert" that shares the same runtime engine)
+   that deals with multiple commits got confused when the final step
+   gets stopped with a conflict and the user concluded the sequence
+   with "git commit".  Attempt to fix it by cleaning up the state
+   files used by these commands in such a situation.
+   (merge 4a72486de9 pw/clean-sequencer-state-upon-final-commit 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).
@@ -385,3 +508,5 @@
    (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).
+   (merge da9ca955a7 jk/ls-files-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/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-branch.txt b/Documentation/git-branch.txt
index 0cd87dd..6ebd512 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -45,7 +45,11 @@
 branch).
 
 The command's second form creates a new branch head named <branchname>
-which points to the current `HEAD`, or <start-point> if given.
+which points to the current `HEAD`, or <start-point> if given. As a
+special case, for <start-point>, you may use `"A...B"` as a shortcut for
+the merge base of `A` and `B` if there is exactly one merge base. You
+can leave out at most one of `A` and `B`, in which case it defaults to
+`HEAD`.
 
 Note that this will create the new branch, but it will not switch the
 working tree to it; use "git checkout <newbranch>" to switch to the
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 877e5f5..964f912 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -313,6 +313,10 @@
 <start_point>::
 	The name of a commit at which to start the new branch; see
 	linkgit:git-branch[1] for details. Defaults to HEAD.
++
+As a special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 <tree-ish>::
 	Tree to checkout from (when paths are given). If not specified,
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-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-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-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/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-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/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 3fc4065..8a5009c 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.21.GIT
+DEF_VER=v2.22.0-rc0
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 9f1b6e8..8a7e235 100644
--- a/Makefile
+++ b/Makefile
@@ -439,6 +439,9 @@
 #
 # Define FILENO_IS_A_MACRO if fileno() is a macro, not a real function.
 #
+# Define NEED_ACCESS_ROOT_HANDLER if access() under root may success for X_OK
+# even if execution permission isn't granted for any user.
+#
 # Define PAGER_ENV to a SP separated VAR=VAL pairs to define
 # default environment variables to be passed when a pager is spawned, e.g.
 #
@@ -592,6 +595,7 @@
 LIB_OBJS =
 PROGRAM_OBJS =
 PROGRAMS =
+EXCLUDED_PROGRAMS =
 SCRIPT_PERL =
 SCRIPT_PYTHON =
 SCRIPT_SH =
@@ -614,7 +618,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 +640,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 +654,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 +669,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 +741,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 +820,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 +1002,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
@@ -1110,7 +1109,6 @@
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/rebase.o
-BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
@@ -1125,7 +1123,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
@@ -1174,8 +1171,11 @@
 SPARSE_FLAGS ?=
 SP_EXTRA_FLAGS =
 
-# For the 'coccicheck' target
+# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
+# usually result in less CPU usage at the cost of higher peak memory.
+# Setting it to 0 will feed all files in a single spatch invocation.
 SPATCH_FLAGS = --all-includes --patch .
+SPATCH_BATCH_SIZE = 1
 
 include config.mak.uname
 -include config.mak.autogen
@@ -1342,6 +1342,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 +1367,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 +1619,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
@@ -1833,6 +1839,11 @@
 	COMPAT_OBJS += compat/fileno.o
 endif
 
+ifdef NEED_ACCESS_ROOT_HANDLER
+	COMPAT_CFLAGS += -DNEED_ACCESS_ROOT_HANDLER
+	COMPAT_OBJS += compat/access.o
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK = NoThanks
 endif
@@ -2133,7 +2144,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 +2479,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 +2725,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
@@ -2790,12 +2810,14 @@
 
 %.cocci.patch: %.cocci $(COCCI_SOURCES)
 	@echo '    ' SPATCH $<; \
-	ret=0; \
-	for f in $(COCCI_SOURCES); do \
-		$(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \
-			{ ret=$$?; break; }; \
-	done >$@+ 2>$@.log; \
-	if test $$ret != 0; \
+	if test $(SPATCH_BATCH_SIZE) = 0; then \
+		limit=; \
+	else \
+		limit='-n $(SPATCH_BATCH_SIZE)'; \
+	fi; \
+	if ! echo $(COCCI_SOURCES) | xargs $$limit \
+		$(SPATCH) --sp-file $< $(SPATCH_FLAGS) \
+		>$@+ 2>$@.log; \
 	then \
 		cat $@.log; \
 		exit 1; \
@@ -2991,7 +3013,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 +3062,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 +3071,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 +3107,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 +3131,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.c b/archive.c
index f2c78a2..53141c1 100644
--- a/archive.c
+++ b/archive.c
@@ -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,
diff --git a/archive.h b/archive.h
index dd022a6..e60e3dd 100644
--- a/archive.h
+++ b/archive.h
@@ -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 c11c516..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)
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.c b/branch.c
index 28b81a7..a594cc2 100644
--- a/branch.c
+++ b/branch.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "refspec.h"
 #include "remote.h"
+#include "sequencer.h"
 #include "commit.h"
 #include "worktree.h"
 
@@ -268,7 +269,7 @@
 	}
 
 	real_ref = NULL;
-	if (get_oid(start_name, &oid)) {
+	if (get_oid_mb(start_name, &oid)) {
 		if (explicit_tracking) {
 			if (advice_set_upstream_failure) {
 				error(_(upstream_missing), start_name);
@@ -339,8 +340,7 @@
 
 void remove_branch_state(struct repository *r)
 {
-	unlink(git_path_cherry_pick_head(r));
-	unlink(git_path_revert_head(r));
+	sequencer_post_commit_cleanup(r);
 	unlink(git_path_merge_head(r));
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
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/clone.c b/builtin/clone.c
index 703b724..85b0d31 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)
@@ -134,6 +135,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"),
@@ -654,7 +657,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;
 
@@ -663,6 +667,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"));
@@ -1133,6 +1138,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,
@@ -1221,7 +1229,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.c b/builtin/commit.c
index 833ecb3..1c9e8e2 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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,24 +1184,7 @@
 		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);
 
@@ -1491,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) "" },
@@ -1627,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();
@@ -1667,8 +1658,7 @@
 		die("%s", err.buf);
 	}
 
-	unlink(git_path_cherry_pick_head(the_repository));
-	unlink(git_path_revert_head(the_repository));
+	sequencer_post_commit_cleanup(the_repository);
 	unlink(git_path_merge_head(the_repository));
 	unlink(git_path_merge_msg(the_repository));
 	unlink(git_path_merge_mode(the_repository));
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/pack-objects.c b/builtin/pack-objects.c
index 2d9a3bd..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)
diff --git a/builtin/pull.c b/builtin/pull.c
index 9bd6a78..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),
@@ -645,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)
@@ -876,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/rebase--interactive.c b/builtin/rebase--interactive.c
deleted file mode 100644
index 4535523..0000000
--- a/builtin/rebase--interactive.c
+++ /dev/null
@@ -1,377 +0,0 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-#include "builtin.h"
-#include "cache.h"
-#include "config.h"
-#include "parse-options.h"
-#include "sequencer.h"
-#include "rebase-interactive.h"
-#include "argv-array.h"
-#include "refs.h"
-#include "rerere.h"
-#include "run-command.h"
-
-static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
-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)
-{
-	const char *base_rev = upstream ? upstream : onto, *shorthead;
-	struct object_id orig_head;
-
-	if (get_oid("HEAD", &orig_head))
-		return error(_("no HEAD?"));
-
-	*head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
-	*revisions = xstrfmt("%s...%s", base_rev, *head_hash);
-
-	shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
-
-	if (upstream) {
-		const char *shortrev;
-		struct object_id rev_oid;
-
-		get_oid(base_rev, &rev_oid);
-		shortrev = find_unique_abbrev(&rev_oid, DEFAULT_ABBREV);
-
-		*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
-	} else
-		*shortrevisions = xstrdup(shorthead);
-
-	return 0;
-}
-
-static int init_basic_state(struct replay_opts *opts, const char *head_name,
-			    const char *onto, const char *orig_head)
-{
-	FILE *interactive;
-
-	if (!is_directory(path_state_dir()) && mkdir_in_gitdir(path_state_dir()))
-		return error_errno(_("could not create temporary %s"), path_state_dir());
-
-	delete_reflog("REBASE_HEAD");
-
-	interactive = fopen(path_interactive(), "w");
-	if (!interactive)
-		return error_errno(_("could not mark as interactive"));
-	fclose(interactive);
-
-	return write_basic_state(opts, head_name, onto, orig_head);
-}
-
-static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
-				 const char *switch_to, const char *upstream,
-				 const char *onto, const char *onto_name,
-				 const char *squash_onto, const char *head_name,
-				 const char *restrict_revision, char *raw_strategies,
-				 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;
-	struct todo_list todo_list = TODO_LIST_INIT;
-
-	if (prepare_branch_to_be_rebased(opts, switch_to))
-		return -1;
-
-	if (get_revision_ranges(upstream, onto, &head_hash,
-				&revisions, &shortrevisions))
-		return -1;
-
-	if (raw_strategies)
-		parse_strategy_opts(opts, raw_strategies);
-
-	if (init_basic_state(opts, head_name, onto, head_hash)) {
-		free(revisions);
-		free(shortrevisions);
-
-		return -1;
-	}
-
-	if (!upstream && squash_onto)
-		write_file(path_squash_onto(), "%s\n", squash_onto);
-
-	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.buf,
-				    make_script_args.argc, make_script_args.argv,
-				    flags);
-
-	if (ret)
-		error(_("could not generate todo list"));
-	else {
-		discard_cache();
-		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;
-}
-
-static const char * const builtin_rebase_interactive_usage[] = {
-	N_("git rebase--interactive [<options>]"),
-	NULL
-};
-
-int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
-{
-	struct replay_opts opts = REPLAY_OPTS_INIT;
-	unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
-	int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
-	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,
-		SHORTEN_OIDS, EXPAND_OIDS, CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC
-	} command = 0;
-	struct option options[] = {
-		OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
-		OPT_BOOL(0, "keep-empty", &keep_empty, N_("keep empty commits")),
-		OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
-			 N_("allow commits with empty messages")),
-		OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
-		OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
-			 N_("keep original branch points of cousins")),
-		OPT_BOOL(0, "autosquash", &autosquash,
-			 N_("move commits that begin with squash!/fixup!")),
-		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
-		OPT__VERBOSE(&opts.verbose, N_("be verbose")),
-		OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
-			    CONTINUE),
-		OPT_CMDMODE(0, "skip", &command, N_("skip commit"), SKIP),
-		OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
-			    EDIT_TODO),
-		OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
-			    SHOW_CURRENT_PATCH),
-		OPT_CMDMODE(0, "shorten-ids", &command,
-			N_("shorten commit ids in the todo list"), SHORTEN_OIDS),
-		OPT_CMDMODE(0, "expand-ids", &command,
-			N_("expand commit ids in the todo list"), EXPAND_OIDS),
-		OPT_CMDMODE(0, "check-todo-list", &command,
-			N_("check the todo list"), CHECK_TODO_LIST),
-		OPT_CMDMODE(0, "rearrange-squash", &command,
-			N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
-		OPT_CMDMODE(0, "add-exec-commands", &command,
-			N_("insert exec commands in todo list"), ADD_EXEC),
-		OPT_STRING(0, "onto", &onto, N_("onto"), N_("onto")),
-		OPT_STRING(0, "restrict-revision", &restrict_revision,
-			   N_("restrict-revision"), N_("restrict revision")),
-		OPT_STRING(0, "squash-onto", &squash_onto, N_("squash-onto"),
-			   N_("squash onto")),
-		OPT_STRING(0, "upstream", &upstream, N_("upstream"),
-			   N_("the upstream commit")),
-		OPT_STRING(0, "head-name", &head_name, N_("head-name"), N_("head name")),
-		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign, N_("key-id"),
-			N_("GPG-sign commits"),
-			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
-			   N_("rebase strategy")),
-		OPT_STRING(0, "strategy-opts", &raw_strategies, N_("strategy-opts"),
-			   N_("strategy options")),
-		OPT_STRING(0, "switch-to", &switch_to, N_("switch-to"),
-			   N_("the branch or commit to checkout")),
-		OPT_STRING(0, "onto-name", &onto_name, N_("onto-name"), N_("onto name")),
-		OPT_STRING(0, "cmd", &cmd, N_("cmd"), N_("the command to run")),
-		OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_auto),
-		OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
-			 N_("automatically re-schedule any `exec` that fails")),
-		OPT_END()
-	};
-
-	sequencer_init_config(&opts);
-	git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
-
-	opts.action = REPLAY_INTERACTIVE_REBASE;
-	opts.allow_ff = 1;
-	opts.allow_empty = 1;
-
-	if (argc == 1)
-		usage_with_options(builtin_rebase_interactive_usage, options);
-
-	argc = parse_options(argc, argv, NULL, options,
-			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
-
-	opts.gpg_sign = xstrdup_or_null(opts.gpg_sign);
-
-	flags |= keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
-	flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
-	flags |= rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
-	flags |= rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
-	flags |= command == SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
-
-	if (rebase_cousins >= 0 && !rebase_merges)
-		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)
-			die(_("a base commit must be provided with --upstream or --onto"));
-
-		ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
-					    onto_name, squash_onto, head_name, restrict_revision,
-					    raw_strategies, &commands, autosquash);
-		break;
-	case SKIP: {
-		struct string_list merge_rr = STRING_LIST_INIT_DUP;
-
-		rerere_clear(the_repository, &merge_rr);
-		/* fallthrough */
-	case CONTINUE:
-		ret = sequencer_continue(the_repository, &opts);
-		break;
-	}
-	case EDIT_TODO:
-		ret = edit_todo_file(flags);
-		break;
-	case SHOW_CURRENT_PATCH: {
-		struct child_process cmd = CHILD_PROCESS_INIT;
-
-		cmd.git_cmd = 1;
-		argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
-		ret = run_command(&cmd);
-
-		break;
-	}
-	case SHORTEN_OIDS:
-	case EXPAND_OIDS:
-		ret = transform_todo_file(flags);
-		break;
-	case CHECK_TODO_LIST:
-		ret = check_todo_list_from_file(the_repository);
-		break;
-	case REARRANGE_SQUASH:
-		ret = rearrange_squash_in_todo_file();
-		break;
-	case ADD_EXEC:
-		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..ba3a574 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -25,6 +25,8 @@
 #include "commit-reach.h"
 #include "rerere.h"
 #include "branch.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
 
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -35,6 +37,8 @@
 	NULL
 };
 
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 static GIT_PATH_FUNC(apply_dir, "rebase-apply")
 static GIT_PATH_FUNC(merge_dir, "rebase-merge")
 
@@ -86,6 +90,437 @@
 	int use_legacy_rebase;
 };
 
+#define REBASE_OPTIONS_INIT {			  	\
+		.type = REBASE_UNSPECIFIED,	  	\
+		.flags = REBASE_NO_QUIET, 		\
+		.git_am_opts = ARGV_ARRAY_INIT,		\
+		.git_format_patch_opt = STRBUF_INIT	\
+	}
+
+static struct replay_opts get_replay_opts(const struct rebase_options *opts)
+{
+	struct replay_opts replay = REPLAY_OPTS_INIT;
+
+	replay.action = REPLAY_INTERACTIVE_REBASE;
+	sequencer_init_config(&replay);
+
+	replay.signoff = opts->signoff;
+	replay.allow_ff = !(opts->flags & REBASE_FORCE);
+	if (opts->allow_rerere_autoupdate)
+		replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
+	replay.allow_empty = 1;
+	replay.allow_empty_message = opts->allow_empty_message;
+	replay.verbose = opts->flags & REBASE_VERBOSE;
+	replay.reschedule_failed_exec = opts->reschedule_failed_exec;
+	replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
+	replay.strategy = opts->strategy;
+	if (opts->strategy_opts)
+		parse_strategy_opts(&replay, opts->strategy_opts);
+
+	return replay;
+}
+
+enum action {
+	ACTION_NONE = 0,
+	ACTION_CONTINUE,
+	ACTION_SKIP,
+	ACTION_ABORT,
+	ACTION_QUIT,
+	ACTION_EDIT_TODO,
+	ACTION_SHOW_CURRENT_PATCH,
+	ACTION_SHORTEN_OIDS,
+	ACTION_EXPAND_OIDS,
+	ACTION_CHECK_TODO_LIST,
+	ACTION_REARRANGE_SQUASH,
+	ACTION_ADD_EXEC
+};
+
+static const char *action_names[] = { "undefined",
+				      "continue",
+				      "skip",
+				      "abort",
+				      "quit",
+				      "edit_todo",
+				      "show_current_patch" };
+
+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(struct commit *upstream, struct commit *onto,
+			       const char **head_hash,
+			       char **revisions, char **shortrevisions)
+{
+	struct commit *base_rev = upstream ? upstream : onto;
+	const char *shorthead;
+	struct object_id orig_head;
+
+	if (get_oid("HEAD", &orig_head))
+		return error(_("no HEAD?"));
+
+	*head_hash = find_unique_abbrev(&orig_head, GIT_MAX_HEXSZ);
+	*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
+						   *head_hash);
+
+	shorthead = find_unique_abbrev(&orig_head, DEFAULT_ABBREV);
+
+	if (upstream) {
+		const char *shortrev;
+
+		shortrev = find_unique_abbrev(&base_rev->object.oid,
+					      DEFAULT_ABBREV);
+
+		*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
+	} else
+		*shortrevisions = xstrdup(shorthead);
+
+	return 0;
+}
+
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+			    struct commit *onto, const char *orig_head)
+{
+	FILE *interactive;
+
+	if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
+		return error_errno(_("could not create temporary %s"), merge_dir());
+
+	delete_reflog("REBASE_HEAD");
+
+	interactive = fopen(path_interactive(), "w");
+	if (!interactive)
+		return error_errno(_("could not mark as interactive"));
+	fclose(interactive);
+
+	return write_basic_state(opts, head_name, onto, orig_head);
+}
+
+static void split_exec_commands(const char *cmd, struct string_list *commands)
+{
+	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);
+	}
+}
+
+static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
+{
+	int ret;
+	const char *head_hash = NULL;
+	char *revisions = NULL, *shortrevisions = NULL;
+	struct argv_array make_script_args = ARGV_ARRAY_INIT;
+	struct todo_list todo_list = TODO_LIST_INIT;
+	struct replay_opts replay = get_replay_opts(opts);
+	struct string_list commands = STRING_LIST_INIT_DUP;
+
+	if (prepare_branch_to_be_rebased(the_repository, &replay,
+					 opts->switch_to))
+		return -1;
+
+	if (get_revision_ranges(opts->upstream, opts->onto, &head_hash,
+				&revisions, &shortrevisions))
+		return -1;
+
+	if (init_basic_state(&replay,
+			     opts->head_name ? opts->head_name : "detached HEAD",
+			     opts->onto, head_hash)) {
+		free(revisions);
+		free(shortrevisions);
+
+		return -1;
+	}
+
+	if (!opts->upstream && opts->squash_onto)
+		write_file(path_squash_onto(), "%s\n",
+			   oid_to_hex(opts->squash_onto));
+
+	argv_array_pushl(&make_script_args, "", revisions, NULL);
+	if (opts->restrict_revision)
+		argv_array_push(&make_script_args,
+				oid_to_hex(&opts->restrict_revision->object.oid));
+
+	ret = sequencer_make_script(the_repository, &todo_list.buf,
+				    make_script_args.argc, make_script_args.argv,
+				    flags);
+
+	if (ret)
+		error(_("could not generate todo list"));
+	else {
+		discard_cache();
+		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+						&todo_list))
+			BUG("unusable todo list");
+
+		split_exec_commands(opts->cmd, &commands);
+		ret = complete_action(the_repository, &replay, flags,
+			shortrevisions, opts->onto_name, opts->onto, head_hash,
+			&commands, opts->autosquash, &todo_list);
+	}
+
+	string_list_clear(&commands, 0);
+	free(revisions);
+	free(shortrevisions);
+	todo_list_release(&todo_list);
+	argv_array_clear(&make_script_args);
+
+	return ret;
+}
+
+static int run_rebase_interactive(struct rebase_options *opts,
+				  enum action command)
+{
+	unsigned flags = 0;
+	int abbreviate_commands = 0, ret = 0;
+
+	git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
+
+	flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+	flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
+	flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
+	flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+	flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+
+	switch (command) {
+	case ACTION_NONE: {
+		if (!opts->onto && !opts->upstream)
+			die(_("a base commit must be provided with --upstream or --onto"));
+
+		ret = do_interactive_rebase(opts, flags);
+		break;
+	}
+	case ACTION_SKIP: {
+		struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+		rerere_clear(the_repository, &merge_rr);
+	}
+		/* fallthrough */
+	case ACTION_CONTINUE: {
+		struct replay_opts replay_opts = get_replay_opts(opts);
+
+		ret = sequencer_continue(the_repository, &replay_opts);
+		break;
+	}
+	case ACTION_EDIT_TODO:
+		ret = edit_todo_file(flags);
+		break;
+	case ACTION_SHOW_CURRENT_PATCH: {
+		struct child_process cmd = CHILD_PROCESS_INIT;
+
+		cmd.git_cmd = 1;
+		argv_array_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
+		ret = run_command(&cmd);
+
+		break;
+	}
+	case ACTION_SHORTEN_OIDS:
+	case ACTION_EXPAND_OIDS:
+		ret = transform_todo_file(flags);
+		break;
+	case ACTION_CHECK_TODO_LIST:
+		ret = check_todo_list_from_file(the_repository);
+		break;
+	case ACTION_REARRANGE_SQUASH:
+		ret = rearrange_squash_in_todo_file();
+		break;
+	case ACTION_ADD_EXEC: {
+		struct string_list commands = STRING_LIST_INIT_DUP;
+
+		split_exec_commands(opts->cmd, &commands);
+		ret = add_exec_commands(&commands);
+		string_list_clear(&commands, 0);
+		break;
+	}
+	default:
+		BUG("invalid command '%d'", command);
+	}
+
+	return ret;
+}
+
+static const char * const builtin_rebase_interactive_usage[] = {
+	N_("git rebase--interactive [<options>]"),
+	NULL
+};
+
+int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
+{
+	struct rebase_options opts = REBASE_OPTIONS_INIT;
+	struct object_id squash_onto = null_oid;
+	enum action command = ACTION_NONE;
+	struct option options[] = {
+		OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
+			   REBASE_FORCE),
+		OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+		OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
+			 N_("allow commits with empty messages")),
+		OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
+		OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
+			 N_("keep original branch points of cousins")),
+		OPT_BOOL(0, "autosquash", &opts.autosquash,
+			 N_("move commits that begin with squash!/fixup!")),
+		OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
+		OPT_BIT('v', "verbose", &opts.flags,
+			N_("display a diffstat of what changed upstream"),
+			REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+		OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
+			    ACTION_CONTINUE),
+		OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
+		OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
+			    ACTION_EDIT_TODO),
+		OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
+			    ACTION_SHOW_CURRENT_PATCH),
+		OPT_CMDMODE(0, "shorten-ids", &command,
+			N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
+		OPT_CMDMODE(0, "expand-ids", &command,
+			N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
+		OPT_CMDMODE(0, "check-todo-list", &command,
+			N_("check the todo list"), ACTION_CHECK_TODO_LIST),
+		OPT_CMDMODE(0, "rearrange-squash", &command,
+			N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
+		OPT_CMDMODE(0, "add-exec-commands", &command,
+			N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
+		{ OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
+		  PARSE_OPT_NONEG, parse_opt_commit, 0 },
+		{ OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
+		  N_("restrict-revision"), N_("restrict revision"),
+		  PARSE_OPT_NONEG, parse_opt_commit, 0 },
+		{ OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
+		  N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
+		{ OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
+		  N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
+		  0 },
+		OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
+		{ OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
+			N_("GPG-sign commits"),
+			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+		OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
+			   N_("rebase strategy")),
+		OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
+			   N_("strategy options")),
+		OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
+			   N_("the branch or commit to checkout")),
+		OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
+		OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
+		OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
+		OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
+			 N_("automatically re-schedule any `exec` that fails")),
+		OPT_END()
+	};
+
+	opts.rebase_cousins = -1;
+
+	if (argc == 1)
+		usage_with_options(builtin_rebase_interactive_usage, options);
+
+	argc = parse_options(argc, argv, NULL, options,
+			builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
+
+	if (!is_null_oid(&squash_onto))
+		opts.squash_onto = &squash_onto;
+
+	if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
+		warning(_("--[no-]rebase-cousins has no effect without "
+			  "--rebase-merges"));
+
+	return !!run_rebase_interactive(&opts, command);
+}
+
 static int is_interactive(struct rebase_options *opts)
 {
 	return opts->type == REBASE_INTERACTIVE ||
@@ -184,14 +619,13 @@
 			    &buf))
 			return -1;
 		if (!strcmp(buf.buf, "--rerere-autoupdate"))
-			opts->allow_rerere_autoupdate = 1;
+			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
 		else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
-			opts->allow_rerere_autoupdate = 0;
+			opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
 		else
 			warning(_("ignoring invalid allow_rerere_autoupdate: "
 				  "'%s'"), buf.buf);
-	} else
-		opts->allow_rerere_autoupdate = -1;
+	}
 
 	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
 		strbuf_reset(&buf);
@@ -223,7 +657,7 @@
 	return 0;
 }
 
-static int write_basic_state(struct rebase_options *opts)
+static int rebase_write_basic_state(struct rebase_options *opts)
 {
 	write_file(state_dir_path("head-name", opts), "%s",
 		   opts->head_name ? opts->head_name : "detached HEAD");
@@ -241,10 +675,11 @@
 	if (opts->strategy_opts)
 		write_file(state_dir_path("strategy_opts", opts), "%s",
 			   opts->strategy_opts);
-	if (opts->allow_rerere_autoupdate >= 0)
+	if (opts->allow_rerere_autoupdate > 0)
 		write_file(state_dir_path("allow_rerere_autoupdate", opts),
 			   "-%s-rerere-autoupdate",
-			   opts->allow_rerere_autoupdate ? "" : "-no");
+			   opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
+				"" : "-no");
 	if (opts->gpg_sign_opt)
 		write_file(state_dir_path("gpg_sign_opt", opts), "%s",
 			   opts->gpg_sign_opt);
@@ -608,9 +1043,9 @@
 	argv_array_push(&am.args, "--rebasing");
 	argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
 	argv_array_push(&am.args, "--patch-format=mboxrd");
-	if (opts->allow_rerere_autoupdate > 0)
+	if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
 		argv_array_push(&am.args, "--rerere-autoupdate");
-	else if (opts->allow_rerere_autoupdate == 0)
+	else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
 		argv_array_push(&am.args, "--no-rerere-autoupdate");
 	if (opts->gpg_sign_opt)
 		argv_array_push(&am.args, opts->gpg_sign_opt);
@@ -623,12 +1058,12 @@
 	}
 
 	if (is_directory(opts->state_dir))
-		write_basic_state(opts);
+		rebase_write_basic_state(opts);
 
 	return status;
 }
 
-static int run_specific_rebase(struct rebase_options *opts)
+static int run_specific_rebase(struct rebase_options *opts, enum action action)
 {
 	const char *argv[] = { NULL, NULL };
 	struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
@@ -637,77 +1072,19 @@
 
 	if (opts->type == REBASE_INTERACTIVE) {
 		/* Run builtin interactive rebase */
-		struct child_process child = CHILD_PROCESS_INIT;
-
-		argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
-				 resolvemsg);
+		setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
 		if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
-			argv_array_push(&child.env_array,
-					"GIT_SEQUENCE_EDITOR=:");
+			setenv("GIT_SEQUENCE_EDITOR", ":", 1);
 			opts->autosquash = 0;
 		}
+		if (opts->gpg_sign_opt) {
+			/* remove the leading "-S" */
+			char *tmp = xstrdup(opts->gpg_sign_opt + 2);
+			free(opts->gpg_sign_opt);
+			opts->gpg_sign_opt = tmp;
+		}
 
-		child.git_cmd = 1;
-		argv_array_push(&child.args, "rebase--interactive");
-
-		if (opts->action)
-			argv_array_pushf(&child.args, "--%s", opts->action);
-		if (opts->keep_empty)
-			argv_array_push(&child.args, "--keep-empty");
-		if (opts->rebase_merges)
-			argv_array_push(&child.args, "--rebase-merges");
-		if (opts->rebase_cousins)
-			argv_array_push(&child.args, "--rebase-cousins");
-		if (opts->autosquash)
-			argv_array_push(&child.args, "--autosquash");
-		if (opts->flags & REBASE_VERBOSE)
-			argv_array_push(&child.args, "--verbose");
-		if (opts->flags & REBASE_FORCE)
-			argv_array_push(&child.args, "--no-ff");
-		if (opts->restrict_revision)
-			argv_array_pushf(&child.args,
-					 "--restrict-revision=^%s",
-					 oid_to_hex(&opts->restrict_revision->object.oid));
-		if (opts->upstream)
-			argv_array_pushf(&child.args, "--upstream=%s",
-					 oid_to_hex(&opts->upstream->object.oid));
-		if (opts->onto)
-			argv_array_pushf(&child.args, "--onto=%s",
-					 oid_to_hex(&opts->onto->object.oid));
-		if (opts->squash_onto)
-			argv_array_pushf(&child.args, "--squash-onto=%s",
-					 oid_to_hex(opts->squash_onto));
-		if (opts->onto_name)
-			argv_array_pushf(&child.args, "--onto-name=%s",
-					 opts->onto_name);
-		argv_array_pushf(&child.args, "--head-name=%s",
-				 opts->head_name ?
-				 opts->head_name : "detached HEAD");
-		if (opts->strategy)
-			argv_array_pushf(&child.args, "--strategy=%s",
-					 opts->strategy);
-		if (opts->strategy_opts)
-			argv_array_pushf(&child.args, "--strategy-opts=%s",
-					 opts->strategy_opts);
-		if (opts->switch_to)
-			argv_array_pushf(&child.args, "--switch-to=%s",
-					 opts->switch_to);
-		if (opts->cmd)
-			argv_array_pushf(&child.args, "--cmd=%s", opts->cmd);
-		if (opts->allow_empty_message)
-			argv_array_push(&child.args, "--allow-empty-message");
-		if (opts->allow_rerere_autoupdate > 0)
-			argv_array_push(&child.args, "--rerere-autoupdate");
-		else if (opts->allow_rerere_autoupdate == 0)
-			argv_array_push(&child.args, "--no-rerere-autoupdate");
-		if (opts->gpg_sign_opt)
-			argv_array_push(&child.args, opts->gpg_sign_opt);
-		if (opts->signoff)
-			argv_array_push(&child.args, "--signoff");
-		if (opts->reschedule_failed_exec)
-			argv_array_push(&child.args, "--reschedule-failed-exec");
-
-		status = run_command(&child);
+		status = run_rebase_interactive(opts, action);
 		goto finished_rebase;
 	}
 
@@ -747,9 +1124,9 @@
 	add_var(&script_snippet, "action", opts->action ? opts->action : "");
 	add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
 	add_var(&script_snippet, "allow_rerere_autoupdate",
-		opts->allow_rerere_autoupdate < 0 ? "" :
 		opts->allow_rerere_autoupdate ?
-		"--rerere-autoupdate" : "--no-rerere-autoupdate");
+			opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
+			"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
 	add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
 	add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
 	add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
@@ -991,14 +1368,7 @@
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-	struct rebase_options options = {
-		.type = REBASE_UNSPECIFIED,
-		.flags = REBASE_NO_QUIET,
-		.git_am_opts = ARGV_ARRAY_INIT,
-		.allow_rerere_autoupdate  = -1,
-		.allow_empty_message = 1,
-		.git_format_patch_opt = STRBUF_INIT,
-	};
+	struct rebase_options options = REBASE_OPTIONS_INIT;
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1006,23 +1376,7 @@
 	struct strbuf revisions = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct object_id merge_base;
-	enum {
-		NO_ACTION,
-		ACTION_CONTINUE,
-		ACTION_SKIP,
-		ACTION_ABORT,
-		ACTION_QUIT,
-		ACTION_EDIT_TODO,
-		ACTION_SHOW_CURRENT_PATCH,
-	} action = NO_ACTION;
-	static const char *action_names[] = { N_("undefined"),
-					      N_("continue"),
-					      N_("skip"),
-					      N_("abort"),
-					      N_("quit"),
-					      N_("edit_todo"),
-					      N_("show_current_patch"),
-					      NULL };
+	enum action action = ACTION_NONE;
 	const char *gpg_sign = NULL;
 	struct string_list exec = STRING_LIST_INIT_NODUP;
 	const char *rebase_merges = NULL;
@@ -1090,10 +1444,7 @@
 		OPT_SET_INT('p', "preserve-merges", &options.type,
 			    N_("(DEPRECATED) try to recreate merges instead of "
 			       "ignoring them"), REBASE_PRESERVE_MERGES),
-		OPT_BOOL(0, "rerere-autoupdate",
-			 &options.allow_rerere_autoupdate,
-			 N_("allow rerere to update index with resolved "
-			    "conflict")),
+		OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
 		OPT_BOOL('k', "keep-empty", &options.keep_empty,
 			 N_("preserve empty commits during rebase")),
 		OPT_BOOL(0, "autosquash", &options.autosquash,
@@ -1139,6 +1490,7 @@
 	trace_repo_setup(prefix);
 	setup_work_tree();
 
+	options.allow_empty_message = 1;
 	git_config(rebase_config, &options);
 
 	if (options.use_legacy_rebase ||
@@ -1180,7 +1532,7 @@
 			     builtin_rebase_options,
 			     builtin_rebase_usage, 0);
 
-	if (action != NO_ACTION && total_argc != 2) {
+	if (action != ACTION_NONE && total_argc != 2) {
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
 	}
@@ -1193,7 +1545,7 @@
 		warning(_("git rebase --preserve-merges is deprecated. "
 			  "Use --rebase-merges instead."));
 
-	if (action != NO_ACTION && !in_progress)
+	if (action != ACTION_NONE && !in_progress)
 		die(_("No rebase in progress?"));
 	setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
 
@@ -1293,7 +1645,7 @@
 		options.action = "show-current-patch";
 		options.dont_finish_rebase = 1;
 		goto run_rebase;
-	case NO_ACTION:
+	case ACTION_NONE:
 		break;
 	default:
 		BUG("action: %d", action);
@@ -1568,8 +1920,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 +2120,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",
@@ -1792,7 +2143,7 @@
 	options.revisions = revisions.buf;
 
 run_rebase:
-	ret = !!run_specific_rebase(&options);
+	ret = !!run_specific_rebase(&options, action);
 
 cleanup:
 	strbuf_release(&revisions);
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/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/submodule--helper.c b/builtin/submodule--helper.c
index 8c72ea8..0bf4aa0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1301,7 +1301,7 @@
 				die(_("submodule '%s' cannot add alternate: %s"),
 				    sas->submodule_name, err.buf);
 			case SUBMODULE_ALTERNATE_ERROR_INFO:
-				fprintf(stderr, _("submodule '%s' cannot add alternate: %s"),
+				fprintf_ln(stderr, _("submodule '%s' cannot add alternate: %s"),
 					sas->submodule_name, err.buf);
 			case SUBMODULE_ALTERNATE_ERROR_IGNORE:
 				; /* nothing */
diff --git a/builtin/tag.c b/builtin/tag.c
index ad97595..ef37dcc 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 tag 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),
@@ -549,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 890c251..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;
 
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 e928fe9..fa8ede9 100644
--- a/cache.h
+++ b/cache.h
@@ -352,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 */
@@ -545,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
@@ -554,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
@@ -588,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
@@ -598,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
@@ -617,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
@@ -634,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)
 
@@ -674,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)
@@ -706,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,
@@ -719,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.
@@ -746,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 */
@@ -755,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
@@ -777,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
@@ -798,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
@@ -821,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 */
@@ -834,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;
@@ -1022,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
@@ -1047,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;
@@ -1244,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)
 {
@@ -1288,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)
@@ -1333,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.
@@ -1380,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
@@ -1404,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,
@@ -1440,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
@@ -1471,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,
@@ -1542,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;
@@ -1575,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
@@ -1592,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;
@@ -1608,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;
@@ -1629,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;
@@ -1656,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
@@ -1674,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
@@ -1686,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;
@@ -1695,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)
 {
@@ -1721,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.
@@ -1731,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;
@@ -1784,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 */
@@ -1860,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 66865ac..7c5e548 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -267,8 +267,10 @@
 		last_chunk_offset = chunk_offset;
 	}
 
-	if (verify_commit_graph_lite(graph))
+	if (verify_commit_graph_lite(graph)) {
+		free(graph);
 		return NULL;
+	}
 
 	return graph;
 }
@@ -397,6 +399,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)
@@ -410,7 +417,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);
@@ -496,7 +503,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;
 }
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 8f1f39f..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;
@@ -149,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 *);
 
 /*
@@ -183,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,
@@ -195,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.
@@ -246,22 +247,22 @@
 
 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
@@ -282,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;
@@ -305,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".
@@ -332,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
@@ -358,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
@@ -369,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;
@@ -389,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/access.c b/compat/access.c
new file mode 100644
index 0000000..19fda3e
--- /dev/null
+++ b/compat/access.c
@@ -0,0 +1,31 @@
+#define COMPAT_CODE_ACCESS
+#include "../git-compat-util.h"
+
+/* Do the same thing access(2) does, but use the effective uid,
+ * and don't make the mistake of telling root that any file is
+ * executable.  This version uses stat(2).
+ */
+int git_access(const char *path, int mode)
+{
+	struct stat st;
+
+	/* do not interfere a normal user */
+	if (geteuid())
+		return access(path, mode);
+
+	if (stat(path, &st) < 0)
+		return -1;
+
+	/* Root can read or write any file. */
+	if (!(mode & X_OK))
+		return 0;
+
+	/* Root can execute any file that has any one of the execute
+	 * bits set.
+	 */
+	if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+		return 0;
+
+	errno = EACCES;
+	return -1;
+}
diff --git a/compat/fileno.c b/compat/fileno.c
index 7b105f4..8e80ef3 100644
--- a/compat/fileno.c
+++ b/compat/fileno.c
@@ -1,4 +1,4 @@
-#define COMPAT_CODE
+#define COMPAT_CODE_FILENO
 #include "../git-compat-util.h"
 
 int git_fileno(FILE *stream)
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 3605fea..b71688e 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -271,6 +271,8 @@
 	INTERNAL_QSORT = UnfortunatelyYes
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -D_LARGE_FILES
+	FILENO_IS_A_MACRO = UnfortunatelyYes
+	NEED_ACCESS_ROOT_HANDLER = UnfortunatelyYes
 	ifeq ($(shell expr "$(uname_V)" : '[1234]'),1)
 		NO_PTHREADS = YesPlease
 	else
@@ -394,6 +396,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
@@ -573,13 +576,21 @@
 	ifneq ($(shell expr "$(uname_R)" : '1\.'),2)
 		# MSys2
 		prefix = /usr/
+		# Enable DEP
+		BASIC_LDFLAGS += -Wl,--nxcompat
+		# Enable ASLR (unless debugging)
+		ifneq (,$(findstring -O,$(filter-out -O0 -Og,$(CFLAGS))))
+			BASIC_LDFLAGS += -Wl,--dynamicbase
+		endif
 		ifeq (MINGW32,$(MSYSTEM))
 			prefix = /mingw32
 			HOST_CPU = i686
+			BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup
 		endif
 		ifeq (MINGW64,$(MSYSTEM))
 			prefix = /mingw64
 			HOST_CPU = x86_64
+			BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup
 		else
 			COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
 			BASIC_LDFLAGS += -Wl,--large-address-aware
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/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
index 5367545..7440aa1 100644
--- a/contrib/diff-highlight/DiffHighlight.pm
+++ b/contrib/diff-highlight/DiffHighlight.pm
@@ -4,6 +4,11 @@
 use warnings FATAL => 'all';
 use strict;
 
+# Use the correct value for both UNIX and Windows (/dev/null vs nul)
+use File::Spec;
+
+my $NULL = File::Spec->devnull();
+
 # Highlight by reversing foreground and background. You could do
 # other things like bold or underline if you prefer.
 my @OLD_HIGHLIGHT = (
@@ -134,7 +139,7 @@
 # fallback, which means we will work even if git can't be run.
 sub color_config {
 	my ($key, $default) = @_;
-	my $s = `git config --get-color $key 2>/dev/null`;
+	my $s = `git config --get-color $key 2>$NULL`;
 	return length($s) ? $s : $default;
 }
 
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/dir.c b/dir.c
index ad30a5a..ba4a51c 100644
--- a/dir.c
+++ b/dir.c
@@ -1466,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;
 	}
@@ -2314,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)
@@ -2728,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_add3(sizeof(*untracked), len, 1));
+
+	*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;
diff --git a/dir.h b/dir.h
index 823bae6..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);
-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/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..cc0e7e9 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];
@@ -1236,12 +1237,22 @@
 
 #ifdef FILENO_IS_A_MACRO
 int git_fileno(FILE *stream);
-# ifndef COMPAT_CODE
+# ifndef COMPAT_CODE_FILENO
 #  undef fileno
 #  define fileno(p) git_fileno(p)
 # endif
 #endif
 
+#ifdef NEED_ACCESS_ROOT_HANDLER
+int git_access(const char *path, int mode);
+# ifndef COMPAT_CODE_ACCESS
+#  ifdef access
+#  undef access
+#  endif
+#  define access(path, mode) git_access(path, mode)
+# endif
+#endif
+
 /*
  * Our code often opens a path to an optional file, to work on its
  * contents when we can successfully open it.  We can ignore a failure
@@ -1257,7 +1268,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 +1291,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-submodule.sh b/git-submodule.sh
index e3c054b..c7f58c5 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -232,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
diff --git a/git.c b/git.c
index 50da125..1bf9c94 100644
--- a/git.c
+++ b/git.c
@@ -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/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/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.h b/http.h
index ddfe52f..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 {
@@ -241,12 +241,12 @@
 	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 a09163b..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) {							\
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 6126773..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);
@@ -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/notes.c b/notes.c
index be72780..532ec37 100644
--- a/notes.c
+++ b/notes.c
@@ -988,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)
diff --git a/object-store.h b/object-store.h
index b086f5e..46a1da7 100644
--- a/object-store.h
+++ b/object-store.h
@@ -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 */
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/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 cdf6b6e..9f52af9 100644
--- a/packfile.c
+++ b/packfile.c
@@ -903,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)
diff --git a/packfile.h b/packfile.h
index 12baa61..b678d35 100644
--- a/packfile.h
+++ b/packfile.h
@@ -63,7 +63,8 @@
  * 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)
 
 struct packed_git *find_sha1_pack(const unsigned char *sha1,
 				  struct packed_git *packs);
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 6e2e8d6..4b95d04 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -96,6 +96,23 @@
 	return 0;
 }
 
+int parse_opt_commit(const struct option *opt, const char *arg, int unset)
+{
+	struct object_id oid;
+	struct commit *commit;
+	struct commit **target = opt->value;
+
+	if (!arg)
+		return -1;
+	if (get_oid(arg, &oid))
+		return error("malformed object name %s", arg);
+	commit = lookup_commit_reference(the_repository, &oid);
+	if (!commit)
+		return error("no such commit %s", arg);
+	*target = commit;
+	return 0;
+}
+
 int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
 {
 	struct object_id oid;
@@ -112,6 +129,23 @@
 	return 0;
 }
 
+int parse_opt_object_id(const struct option *opt, const char *arg, int unset)
+{
+	struct object_id oid;
+	struct object_id *target = opt->value;
+
+	if (unset) {
+		*target = null_oid;
+		return 0;
+	}
+	if (!arg)
+		return -1;
+	if (get_oid(arg, &oid))
+		return error(_("malformed object name '%s'"), arg);
+	*target = oid;
+	return 0;
+}
+
 int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
 {
 	int *target = opt->value;
diff --git a/parse-options.h b/parse-options.h
index bb49efc..98ea42f 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -283,8 +283,12 @@
 int parse_opt_expiry_date_cb(const struct option *, const char *, int);
 int parse_opt_color_flag_cb(const struct option *, const char *, int);
 int parse_opt_verbosity_cb(const struct option *, const char *, int);
+/* value is struct oid_array* */
 int parse_opt_object_name(const struct option *, const char *, int);
+/* value is struct object_id* */
+int parse_opt_object_id(const struct option *, const char *, int);
 int parse_opt_commits(const struct option *, const char *, int);
+int parse_opt_commit(const struct option *, const char *, int);
 int parse_opt_tertiary(const struct option *, const char *, int);
 int parse_opt_string_list(const struct option *, const char *, int);
 int parse_opt_noop_cb(const struct option *, const char *, int);
@@ -322,5 +326,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/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/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/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 d5a74b1..4fad4e3 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -709,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;
@@ -718,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--;
 	}
@@ -3128,7 +3131,7 @@
 	trace2_region_enter_printf("index", "shared/do_write_index",
 				   the_repository, "%s", (*temp)->filename.buf);
 	ret = do_write_index(si->base, *temp, 1);
-	trace2_region_enter_printf("index", "shared/do_write_index",
+	trace2_region_leave_printf("index", "shared/do_write_index",
 				   the_repository, "%s", (*temp)->filename.buf);
 
 	if (ret)
diff --git a/ref-filter.c b/ref-filter.c
index 8d11a94..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;
 		}
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/remote.c b/remote.c
index 3fe34ea..e50f760 100644
--- a/remote.c
+++ b/remote.c
@@ -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 f58332a..83e8856 100644
--- a/remote.h
+++ b/remote.h
@@ -119,13 +119,13 @@
 #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);
@@ -141,16 +141,16 @@
 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);
 
@@ -255,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);
 
@@ -289,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/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 546f281..f88a97f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -171,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;
@@ -510,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;) {
@@ -582,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;
 }
@@ -901,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;
@@ -971,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))
@@ -1012,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.
@@ -1382,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);
@@ -2105,6 +2168,41 @@
 	return !item->commit;
 }
 
+int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+{
+	struct todo_item item;
+	char *eol;
+	const char *todo_file;
+	struct strbuf buf = STRBUF_INIT;
+	int ret = -1;
+
+	todo_file = git_path_todo_file();
+	if (strbuf_read_file(&buf, todo_file, 0) < 0) {
+		if (errno == ENOENT)
+			return -1;
+		else
+			return error_errno("unable to open '%s'", todo_file);
+	}
+	eol = strchrnul(buf.buf, '\n');
+	if (buf.buf != eol && eol[-1] == '\r')
+		eol--; /* strip Carriage Return */
+	if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
+		goto fail;
+	if (item.command == TODO_PICK)
+		*action = REPLAY_PICK;
+	else if (item.command == TODO_REVERT)
+		*action = REPLAY_REVERT;
+	else
+		goto fail;
+
+	ret = 0;
+
+ fail:
+	strbuf_release(&buf);
+
+	return ret;
+}
+
 int todo_list_parse_insn_buffer(struct repository *r, char *buf,
 				struct todo_list *todo_list)
 {
@@ -2188,6 +2286,57 @@
 	return len;
 }
 
+static int have_finished_the_last_pick(void)
+{
+	struct strbuf buf = STRBUF_INIT;
+	const char *eol;
+	const char *todo_path = git_path_todo_file();
+	int ret = 0;
+
+	if (strbuf_read_file(&buf, todo_path, 0) < 0) {
+		if (errno == ENOENT) {
+			return 0;
+		} else {
+			error_errno("unable to open '%s'", todo_path);
+			return 0;
+		}
+	}
+	/* If there is only one line then we are done */
+	eol = strchr(buf.buf, '\n');
+	if (!eol || !eol[1])
+		ret = 1;
+
+	strbuf_release(&buf);
+
+	return ret;
+}
+
+void sequencer_post_commit_cleanup(struct repository *r)
+{
+	struct replay_opts opts = REPLAY_OPTS_INIT;
+	int need_cleanup = 0;
+
+	if (file_exists(git_path_cherry_pick_head(r))) {
+		unlink(git_path_cherry_pick_head(r));
+		opts.action = REPLAY_PICK;
+		need_cleanup = 1;
+	}
+
+	if (file_exists(git_path_revert_head(r))) {
+		unlink(git_path_revert_head(r));
+		opts.action = REPLAY_REVERT;
+		need_cleanup = 1;
+	}
+
+	if (!need_cleanup)
+		return;
+
+	if (!have_finished_the_last_pick())
+		return;
+
+	sequencer_remove_state(&opts);
+}
+
 static int read_populate_todo(struct repository *r,
 			      struct todo_list *todo_list,
 			      struct replay_opts *opts)
@@ -2303,7 +2452,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)
@@ -2428,14 +2580,15 @@
 }
 
 int write_basic_state(struct replay_opts *opts, const char *head_name,
-		      const char *onto, const char *orig_head)
+		      struct commit *onto, const char *orig_head)
 {
 	const char *quiet = getenv("GIT_QUIET");
 
 	if (head_name)
 		write_file(rebase_path_head_name(), "%s\n", head_name);
 	if (onto)
-		write_file(rebase_path_onto(), "%s\n", onto);
+		write_file(rebase_path_onto(), "%s\n",
+			   oid_to_hex(&onto->object.oid));
 	if (orig_head)
 		write_file(rebase_path_orig_head(), "%s\n", orig_head);
 
@@ -2725,6 +2878,11 @@
 				"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;
 }
 
@@ -3446,10 +3604,11 @@
 	return buf.buf;
 }
 
-static int run_git_checkout(struct replay_opts *opts, const char *commit,
-			    const char *action)
+static int run_git_checkout(struct repository *r, struct replay_opts *opts,
+			    const char *commit, const char *action)
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
+	int ret;
 
 	cmd.git_cmd = 1;
 
@@ -3458,26 +3617,32 @@
 	argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
 
 	if (opts->verbose)
-		return run_command(&cmd);
+		ret = run_command(&cmd);
 	else
-		return run_command_silent_on_success(&cmd);
+		ret = run_command_silent_on_success(&cmd);
+
+	if (!ret)
+		discard_index(r->index);
+
+	return ret;
 }
 
-int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
+int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
+				 const char *commit)
 {
 	const char *action;
 
 	if (commit && *commit) {
 		action = reflog_message(opts, "start", "checkout %s", commit);
-		if (run_git_checkout(opts, commit, action))
+		if (run_git_checkout(r, opts, commit, action))
 			return error(_("could not checkout %s"), commit);
 	}
 
 	return 0;
 }
 
-static int checkout_onto(struct replay_opts *opts,
-			 const char *onto_name, const char *onto,
+static int checkout_onto(struct repository *r, struct replay_opts *opts,
+			 const char *onto_name, const struct object_id *onto,
 			 const char *orig_head)
 {
 	struct object_id oid;
@@ -3486,7 +3651,7 @@
 	if (get_oid(orig_head, &oid))
 		return error(_("%s: not a valid OID"), orig_head);
 
-	if (run_git_checkout(opts, onto, action)) {
+	if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
 		apply_autostash(opts);
 		sequencer_remove_state(opts);
 		return error(_("could not detach HEAD"));
@@ -4762,16 +4927,16 @@
 
 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, struct string_list *commands,
-		    unsigned autosquash, struct todo_list *todo_list)
+		    struct commit *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 new_todo = TODO_LIST_INIT;
 	struct strbuf *buf = &todo_list->buf;
-	struct object_id oid;
+	struct object_id oid = onto->object.oid;
 	int res;
 
-	get_oid(onto, &oid);
 	shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
 
 	if (buf->len == 0) {
@@ -4814,7 +4979,7 @@
 	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);
+		checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
 		todo_list_release(&new_todo);
 
 		return -1;
@@ -4833,7 +4998,7 @@
 
 	todo_list_release(&new_todo);
 
-	if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
+	if (checkout_onto(r, opts, onto_name, &oid, orig_head))
 		return -1;
 
 	if (require_clean_work_tree(r, "rebase", "", 1, 1))
diff --git a/sequencer.h b/sequencer.h
index a515ee4..0c494b8 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -48,6 +48,7 @@
 
 	char *gpg_sign;
 	enum commit_msg_cleanup_mode default_msg_cleanup;
+	int explicit_cleanup;
 
 	/* Merge strategy */
 	char *strategy;
@@ -150,7 +151,7 @@
 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, struct string_list *commands,
+		    struct commit *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);
 
@@ -162,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,
@@ -175,7 +183,8 @@
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
-int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit);
+int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
+				 const char *commit);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
@@ -190,4 +199,7 @@
 
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 int write_basic_state(struct replay_opts *opts, const char *head_name,
-		      const char *onto, const char *orig_head);
+		      struct commit *onto, const char *orig_head);
+void sequencer_post_commit_cleanup(struct repository *r);
+int sequencer_get_last_command(struct repository* r,
+			       enum replay_action *action);
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/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-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/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/perf/README b/t/perf/README
index be12090..c7b70e2 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -45,7 +45,7 @@
 
     $ ./p0001-rev-list.sh
     [...]
-    $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+    $ ./run /path/to/other/git -- ./p0001-rev-list.sh
     [...]
     $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
 
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
index 494907a..66554d2 100755
--- a/t/perf/aggregate.perl
+++ b/t/perf/aggregate.perl
@@ -3,9 +3,9 @@
 use lib '../../perl/build/lib';
 use strict;
 use warnings;
-use JSON;
 use Getopt::Long;
 use Git;
+use Cwd qw(realpath);
 
 sub get_times {
 	my $name = shift;
@@ -99,18 +99,21 @@
 while (scalar @ARGV) {
 	my $arg = $ARGV[0];
 	my $dir;
+	my $prefix = '';
 	last if -f $arg or $arg eq "--";
 	if (! -d $arg) {
 		my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
 		$dir = "build/".$rev;
+	} elsif ($arg eq '.') {
+		$dir = '.';
 	} else {
-		$arg =~ s{/*$}{};
-		$dir = $arg;
-		$dirabbrevs{$dir} = $dir;
+		$dir = realpath($arg);
+		$dirnames{$dir} = $dir;
+		$prefix .= 'bindir';
 	}
 	push @dirs, $dir;
-	$dirnames{$dir} = $arg;
-	my $prefix = $dir;
+	$dirnames{$dir} ||= $arg;
+	$prefix .= $dir;
 	$prefix =~ tr/^a-zA-Z0-9/_/c;
 	$prefixes{$dir} = $prefix . '.';
 	shift @ARGV;
@@ -312,9 +315,6 @@
 		$environment = $reponame;
 	} elsif (exists $ENV{GIT_PERF_REPO_NAME} and $ENV{GIT_PERF_REPO_NAME} ne "") {
 		$environment = $ENV{GIT_PERF_REPO_NAME};
-	} elsif (exists $ENV{GIT_TEST_INSTALLED} and $ENV{GIT_TEST_INSTALLED} ne "") {
-		$environment = $ENV{GIT_TEST_INSTALLED};
-		$environment =~ s|/bin-wrappers$||;
 	} else {
 		$environment = `uname -r`;
 		chomp $environment;
@@ -342,7 +342,8 @@
 		}
 	}
 
-	print to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
+	require JSON;
+	print JSON::to_json(\@data, {utf8 => 1, pretty => 1, canonical => 1}), "\n";
 }
 
 binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
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/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 169f92e..b58a43e 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -21,19 +21,21 @@
 # because it will change our working directory.
 TEST_DIRECTORY=$(pwd)/..
 TEST_OUTPUT_DIRECTORY=$(pwd)
-ABSOLUTE_GIT_TEST_INSTALLED=$(
-	test -n "$GIT_TEST_INSTALLED" && cd "$GIT_TEST_INSTALLED" && pwd)
 
 TEST_NO_CREATE_REPO=t
 TEST_NO_MALLOC_CHECK=t
 
 . ../test-lib.sh
 
-if test -z "$GIT_TEST_INSTALLED"; then
-	perf_results_prefix=
-else
-	perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
-	GIT_TEST_INSTALLED=$ABSOLUTE_GIT_TEST_INSTALLED
+if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+then
+	error "Do not use GIT_TEST_INSTALLED with the perf tests.
+
+Instead use:
+
+    ./run <path-to-git> -- <tests>
+
+See t/perf/README for details."
 fi
 
 # Variables from test-lib that are normally internal to the tests; we
@@ -179,7 +181,7 @@
 		base=$(basename "$0" .sh)
 		echo "$test_count" >>"$perf_results_dir"/$base.subtests
 		echo "$1" >"$perf_results_dir"/$base.$test_count.descr
-		base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+		base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count"
 		"$test_wrapper_func_" "$@"
 	fi
 
diff --git a/t/perf/run b/t/perf/run
index 9aaa733..c7b8610 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -70,6 +70,24 @@
 	) || die "failed to build revision '$mydir'"
 }
 
+set_git_test_installed () {
+	mydir=$1
+
+	mydir_abs=$(cd $mydir && pwd)
+	mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers"
+	if test -d "$mydir_abs_wrappers"
+	then
+		GIT_TEST_INSTALLED=$mydir_abs_wrappers
+	else
+		# Older versions of git lacked bin-wrappers;
+		# fallback to the files in the root.
+		GIT_TEST_INSTALLED=$mydir_abs
+	fi
+	export GIT_TEST_INSTALLED
+	PERF_SET_GIT_TEST_INSTALLED=true
+	export PERF_SET_GIT_TEST_INSTALLED
+}
+
 run_dirs_helper () {
 	mydir=${1%/}
 	shift
@@ -79,7 +97,16 @@
 	if test $# -gt 0 -a "$1" = --; then
 		shift
 	fi
-	if [ ! -d "$mydir" ]; then
+
+	PERF_RESULTS_PREFIX=
+	if test "$mydir" = "."
+	then
+		unset GIT_TEST_INSTALLED
+	elif test -d "$mydir"
+	then
+		PERF_RESULTS_PREFIX=bindir$(cd $mydir && printf "%s" "$(pwd)" | tr -c "[a-zA-Z0-9]" "_").
+		set_git_test_installed "$mydir"
+	else
 		rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
 		die "'$mydir' is neither a directory nor a valid revision"
 		if [ ! -d build/$rev ]; then
@@ -87,16 +114,12 @@
 		fi
 		build_git_rev $rev "$mydir"
 		mydir=build/$rev
+
+		PERF_RESULTS_PREFIX=build_$rev.
+		set_git_test_installed "$mydir"
 	fi
-	if test "$mydir" = .; then
-		unset GIT_TEST_INSTALLED
-	else
-		GIT_TEST_INSTALLED="$mydir/bin-wrappers"
-		# Older versions of git lacked bin-wrappers; fallback to the
-		# files in the root.
-		test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
-		export GIT_TEST_INSTALLED
-	fi
+	export PERF_RESULTS_PREFIX
+
 	run_one_dir "$@"
 }
 
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/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/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/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index c5014ad..822381d 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -60,38 +60,47 @@
 '
 
 test_expect_success 'checkout -b to a new branch, set to HEAD' '
+	test_when_finished "
+		git checkout branch1 &&
+		test_might_fail git branch -D branch2" &&
 	do_checkout branch2
 '
 
-test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
-	git checkout branch1 &&
-	git branch -D branch2 &&
+test_expect_success 'checkout -b to a merge base' '
+	test_when_finished "
+		git checkout branch1 &&
+		test_might_fail git branch -D branch2" &&
+	git checkout -b branch2 branch1...
+'
 
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+	test_when_finished "
+		git checkout branch1 &&
+		test_might_fail git branch -D branch2" &&
 	do_checkout branch2 $HEAD1
 '
 
 test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
-	git checkout branch1 &&
-
-	# clean up from previous test
-	git branch -D branch2 &&
-
 	setup_dirty_unmergeable &&
 	test_must_fail do_checkout branch2 $HEAD1 &&
 	test_dirty_unmergeable
 '
 
 test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+	test_when_finished "
+		git checkout branch1 &&
+		test_might_fail git branch -D branch2" &&
+
 	# still dirty and on branch1
 	do_checkout branch2 $HEAD1 "-f -b" &&
 	test_must_fail test_dirty_unmergeable
 '
 
 test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
-	git checkout branch1 &&
-
-	# clean up from previous test
-	git branch -D branch2 &&
+	test_when_finished "
+		git reset --hard &&
+		git checkout branch1 &&
+		test_might_fail git branch -D branch2" &&
 
 	setup_dirty_mergeable &&
 	do_checkout branch2 $HEAD1 &&
@@ -99,27 +108,18 @@
 '
 
 test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
-	# clean up from previous test
-	git reset --hard &&
-
-	git checkout branch1 &&
-
-	# clean up from previous test
-	git branch -D branch2 &&
-
+	test_when_finished git reset --hard HEAD &&
 	setup_dirty_mergeable &&
 	do_checkout branch2 $HEAD1 "-f -b" &&
 	test_must_fail test_dirty_mergeable
 '
 
 test_expect_success 'checkout -b to an existing branch fails' '
-	git reset --hard HEAD &&
-
+	test_when_finished git reset --hard HEAD &&
 	test_must_fail do_checkout branch2 $HEAD2
 '
 
 test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
-	git reset --hard HEAD &&
 	git checkout branch1 &&
 	git checkout branch2 &&
 	echo  >expect "fatal: A branch named '\''branch1'\'' already exists." &&
@@ -133,6 +133,12 @@
 	do_checkout branch2 "" -B
 '
 
+test_expect_success 'checkout -B to a merge base' '
+	git checkout branch1 &&
+
+	git checkout -B branch2 branch1...
+'
+
 test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
 	git checkout $(git rev-parse --verify HEAD) &&
 
@@ -160,6 +166,7 @@
 '
 
 test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+	test_when_finished git reset --hard &&
 	git checkout branch1 &&
 
 	setup_dirty_mergeable &&
@@ -168,9 +175,6 @@
 '
 
 test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
-	# clean up from previous test
-	git reset --hard &&
-
 	git checkout branch1 &&
 
 	setup_dirty_mergeable &&
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..e9d7084 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -42,6 +42,10 @@
 	git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
 '
 
+test_expect_success 'git branch mb master... should create a branch' '
+	git branch mb master... && test_path_is_file .git/refs/heads/mb
+'
+
 test_expect_success 'git branch HEAD should fail' '
 	test_must_fail git branch HEAD
 '
@@ -264,6 +268,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 &&
@@ -292,8 +320,8 @@
 test_expect_success 'git branch --column' '
 	COLUMNS=81 git branch --column=column >actual &&
 	cat >expected <<\EOF &&
-  a/b/c     bam       foo       l       * master    n         o/p       r
-  abc       bar       j/k       m/m       master2   o/o       q
+  a/b/c     bam       foo       l       * master    mb        o/o       q
+  abc       bar       j/k       m/m       master2   n         o/p       r
 EOF
 	test_cmp expected actual
 '
@@ -315,6 +343,7 @@
   m/m
 * master
   master2
+  mb
   n
   o/o
   o/p
@@ -332,8 +361,8 @@
 	git config --unset column.branch &&
 	git config --unset column.ui &&
 	cat >expected <<\EOF &&
-  a/b/c   bam   foo   l   * master    n     o/p   r
-  abc     bar   j/k   m/m   master2   o/o   q
+  a/b/c   bam   foo   l   * master    mb   o/o   q
+  abc     bar   j/k   m/m   master2   n    o/p   r
 EOF
 	test_cmp expected actual
 '
@@ -357,6 +386,7 @@
   m/m
 * master
   master2
+  mb
   n
   o/o
   o/p
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 1ef8e9d..9b9b4ca 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -93,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
 '
@@ -101,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
 '
@@ -161,6 +161,25 @@
 
 	test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
 '
+test_expect_success 'successful final commit clears cherry-pick state' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick base picked-signed &&
+	echo resolved >foo &&
+	test_path_is_file .git/sequencer/todo &&
+	git commit -a &&
+	test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears cherry-pick state' '
+	pristine_detach initial &&
+
+	test_must_fail git cherry-pick base picked-signed &&
+	echo resolved >foo &&
+	test_path_is_file .git/sequencer/todo &&
+	git reset &&
+	test_must_fail test_path_exists .git/sequencer
+'
 
 test_expect_success 'failed cherry-pick produces dirty index' '
 	pristine_detach initial &&
@@ -180,23 +199,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
 	=======
@@ -206,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 'diff3 -m style' '
 	pristine_detach initial &&
 	git config merge.conflictstyle diff3 &&
-	cat <<-EOF > expected &&
+	cat <<-EOF >expected &&
 	<<<<<<< HEAD
 	a
 	||||||| parent of objid picked
@@ -225,14 +284,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
 	=======
@@ -246,24 +305,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
 '
 
@@ -289,7 +348,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
@@ -321,10 +380,30 @@
 	test_cmp_rev picked REVERT_HEAD
 '
 
+test_expect_success 'successful final commit clears revert state' '
+       pristine_detach picked-signed &&
+
+       test_must_fail git revert picked-signed base &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git commit -a &&
+       test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears revert state' '
+       pristine_detach picked-signed &&
+
+       test_must_fail git revert picked-signed base &&
+       echo resolved >foo &&
+       test_path_is_file .git/sequencer/todo &&
+       git reset &&
+       test_must_fail test_path_exists .git/sequencer
+'
+
 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
@@ -336,10 +415,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 &&
@@ -350,7 +475,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' '
@@ -364,7 +489,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 &&
@@ -383,7 +508,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 &&
@@ -393,7 +518,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
 '
 
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/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/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/t5601-clone.sh b/t/t5601-clone.sh
index 23854ca..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 &&
 	(
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 a089659..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' '
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index b6a995e..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
 '
 
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/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/t7004-tag.sh b/t/t7004-tag.sh
index 0b01862..6aeeb27 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 tag 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/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/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/t7512-status-help.sh b/t/t7512-status-help.sh
index 458608c..c1eb725 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -780,6 +780,24 @@
 	test_i18ncmp expected actual
 '
 
+test_expect_success 'status when cherry-picking after committing conflict resolution' '
+	git reset --hard cherry_branch &&
+	test_when_finished "git cherry-pick --abort" &&
+	test_must_fail git cherry-pick cherry_branch_second one_cherry &&
+	echo end >main.txt &&
+	git commit -a &&
+	cat >expected <<EOF &&
+On branch cherry_branch
+Cherry-pick currently in progress.
+  (run "git cherry-pick --continue" to continue)
+  (use "git cherry-pick --abort" to cancel the cherry-pick operation)
+
+nothing to commit (use -u to show untracked files)
+EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
 test_expect_success 'status showing detached at and from a tag' '
 	test_commit atag tagging &&
 	git checkout atag &&
@@ -857,6 +875,24 @@
 	test_i18ncmp expected actual
 '
 
+test_expect_success 'status while reverting after committing conflict resolution' '
+	test_when_finished "git revert --abort" &&
+	git reset --hard new &&
+	test_must_fail git revert old new &&
+	echo reverted >to-revert.txt &&
+	git commit -a &&
+	cat >expected <<EOF &&
+On branch master
+Revert currently in progress.
+  (run "git revert --continue" to continue)
+  (use "git revert --abort" to cancel the revert operation)
+
+nothing to commit (use -u to show untracked files)
+EOF
+	git status --untracked-files=no >actual &&
+	test_i18ncmp expected actual
+'
+
 test_expect_success 'prepare for different number of commits rebased' '
 	git reset --hard master &&
 	git checkout -b several_commits &&
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/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/test-lib-functions.sh b/t/test-lib-functions.sh
index 788ea1f..8270de7 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1239,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/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..c7b4f14 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)
@@ -717,7 +734,7 @@
 	us_elapsed_region = tr2tls_region_elasped_self(us_now);
 
 	for_each_wanted_builtin (j, tgt_j)
-		if (tgt_j->pfn_data_fl)
+		if (tgt_j->pfn_data_json_fl)
 			tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
 						us_elapsed_region, category,
 						repo, key, value);
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 365ea57..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,
@@ -362,6 +371,7 @@
 		break;
 	case protocol_v1:
 	case protocol_v0:
+		die_if_server_options(transport);
 		refs = fetch_pack(&args, data->fd,
 				  refs_tmp ? refs_tmp : transport->remote_refs,
 				  to_fetch, nr_heads, &data->shallow,
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/upload-pack.c b/upload-pack.c
index cb603a6..d2ea5eb 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -839,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);
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..e065558 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -17,6 +17,7 @@
 #include "utf8.h"
 #include "worktree.h"
 #include "lockfile.h"
+#include "sequencer.h"
 
 static const char cut_line[] =
 "------------------------ >8 ------------------------\n";
@@ -1006,13 +1007,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);
 }
@@ -1380,12 +1387,22 @@
 static void show_cherry_pick_in_progress(struct wt_status *s,
 					 const char *color)
 {
-	status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
-			find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
+	if (is_null_oid(&s->state.cherry_pick_head_oid))
+		status_printf_ln(s, color,
+			_("Cherry-pick currently in progress."));
+	else
+		status_printf_ln(s, color,
+			_("You are currently cherry-picking commit %s."),
+			find_unique_abbrev(&s->state.cherry_pick_head_oid,
+					   DEFAULT_ABBREV));
+
 	if (s->hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
 				_("  (fix conflicts and run \"git cherry-pick --continue\")"));
+		else if (is_null_oid(&s->state.cherry_pick_head_oid))
+			status_printf_ln(s, color,
+				_("  (run \"git cherry-pick --continue\" to continue)"));
 		else
 			status_printf_ln(s, color,
 				_("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
@@ -1398,12 +1415,21 @@
 static void show_revert_in_progress(struct wt_status *s,
 				    const char *color)
 {
-	status_printf_ln(s, color, _("You are currently reverting commit %s."),
-			 find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
+	if (is_null_oid(&s->state.revert_head_oid))
+		status_printf_ln(s, color,
+			_("Revert currently in progress."));
+	else
+		status_printf_ln(s, color,
+			_("You are currently reverting commit %s."),
+			find_unique_abbrev(&s->state.revert_head_oid,
+					   DEFAULT_ABBREV));
 	if (s->hints) {
 		if (has_unmerged(s))
 			status_printf_ln(s, color,
 				_("  (fix conflicts and run \"git revert --continue\")"));
+		else if (is_null_oid(&s->state.revert_head_oid))
+			status_printf_ln(s, color,
+				_("  (run \"git revert --continue\" to continue)"));
 		else
 			status_printf_ln(s, color,
 				_("  (all conflicts fixed: run \"git revert --continue\")"));
@@ -1574,6 +1600,7 @@
 {
 	struct stat st;
 	struct object_id oid;
+	enum replay_action action;
 
 	if (!stat(git_path_merge_head(r), &st)) {
 		wt_status_check_rebase(NULL, state);
@@ -1591,7 +1618,15 @@
 		state->revert_in_progress = 1;
 		oidcpy(&state->revert_head_oid, &oid);
 	}
-
+	if (!sequencer_get_last_command(r, &action)) {
+		if (action == REPLAY_PICK) {
+			state->cherry_pick_in_progress = 1;
+			oidcpy(&state->cherry_pick_head_oid, &null_oid);
+		} else {
+			state->revert_in_progress = 1;
+			oidcpy(&state->revert_head_oid, &null_oid);
+		}
+	}
 	if (get_detached_from)
 		wt_status_get_detached_from(r, state);
 }
@@ -1851,7 +1886,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 +2025,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