Sync with Git 2.30.2 for CVE-2021-21300

Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/.gitattributes b/.gitattributes
index b08a141..b0044cf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,6 +6,7 @@
 *.pm eol=lf diff=perl
 *.py eol=lf diff=python
 *.bat eol=crlf
+CODE_OF_CONDUCT.md -whitespace
 /Documentation/**/*.txt eol=lf
 /command-list.txt eol=lf
 /GIT-VERSION-GEN eol=lf
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a3fbbe6..5f2f884 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -289,7 +289,7 @@
           - jobname: osx-gcc
             cc: gcc
             pool: macos-latest
-          - jobname: GETTEXT_POISON
+          - jobname: linux-gcc-default
             cc: gcc
             pool: ubuntu-latest
     env:
diff --git a/.travis.yml b/.travis.yml
index 05f3e3f..908330a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@
 
 matrix:
   include:
-    - env: jobname=GETTEXT_POISON
+    - env: jobname=linux-gcc-default
       os: linux
       compiler:
       addons:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index fc4645d..65651be 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -8,73 +8,64 @@
 
 ## Our Pledge
 
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to make participation in our project and
-our community a harassment-free experience for everyone, regardless of age,
-body size, disability, ethnicity, sex characteristics, gender identity and
-expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, religion, or sexual identity and
-orientation.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
 
 ## Our Standards
 
-Examples of behavior that contributes to creating a positive environment
-include:
+Examples of behavior that contributes to a positive environment for our
+community include:
 
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+  and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+  overall community
 
-Examples of unacceptable behavior by participants include:
+Examples of unacceptable behavior include:
 
-* The use of sexualized language or imagery and unwelcome sexual attention or
-  advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
+* The use of sexualized language or imagery, and sexual attention or
+  advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
 * Public or private harassment
-* Publishing others' private information, such as a physical or electronic
-  address, without explicit permission
+* Publishing others' private information, such as a physical or email
+  address, without their explicit permission
 * Other conduct which could reasonably be considered inappropriate in a
   professional setting
 
-## Our Responsibilities
+## Enforcement Responsibilities
 
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
 
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
 
 ## Scope
 
-This Code of Conduct applies within all project spaces, and it also applies
-when an individual is representing the project or its community in public
-spaces. Examples of representing a project or community include using an
-official project e-mail address, posting via an official social media account,
-or acting as an appointed representative at an online or offline event.
-Representation of a project may be further defined and clarified by project
-maintainers.
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
 
 ## Enforcement
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at git@sfconservancy.org. All
-complaints will be reviewed and investigated and will result in a response
-that is deemed necessary and appropriate to the circumstances. The project
-team is obligated to maintain confidentiality with regard to the reporter of
-an incident. Further details of specific enforcement policies may be posted
-separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-The project leadership team can be contacted by email as a whole at
+reported to the community leaders responsible for enforcement at
 git@sfconservancy.org, or individually:
 
   - Ævar Arnfjörð Bjarmason <avarab@gmail.com>
@@ -82,12 +73,73 @@
   - Jeff King <peff@peff.net>
   - Junio C Hamano <gitster@pobox.com>
 
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior,  harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
 ## Attribution
 
 This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+version 2.0, available at
+[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
+
+Community Impact Guidelines were inspired by 
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available 
+at [https://www.contributor-covenant.org/translations][translations].
 
 [homepage]: https://www.contributor-covenant.org
+[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
 
-For answers to common questions about this code of conduct, see
-https://www.contributor-covenant.org/faq
diff --git a/Documentation/Makefile b/Documentation/Makefile
index b980407..81d1bf7 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -21,6 +21,7 @@
 MAN5_TXT += gitattributes.txt
 MAN5_TXT += githooks.txt
 MAN5_TXT += gitignore.txt
+MAN5_TXT += gitmailmap.txt
 MAN5_TXT += gitmodules.txt
 MAN5_TXT += gitrepository-layout.txt
 MAN5_TXT += gitweb.conf.txt
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 7c9a037..af0a9da 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -664,7 +664,7 @@
 ----
 test_expect_success 'runs correctly with no args and good output' '
 	git psuh >actual &&
-	test_i18ngrep Pony actual
+	grep Pony actual
 '
 ----
 
diff --git a/Documentation/RelNotes/2.31.0.txt b/Documentation/RelNotes/2.31.0.txt
new file mode 100644
index 0000000..cde22e8
--- /dev/null
+++ b/Documentation/RelNotes/2.31.0.txt
@@ -0,0 +1,363 @@
+Git 2.31 Release Notes
+======================
+
+Updates since v2.30
+-------------------
+
+Backward incompatible and other important changes
+
+ * The "pack-redundant" command, which has been left stale with almost
+   unusable performance issues, now warns loudly when it gets used, as
+   we no longer want to recommend its use (instead just "repack -d"
+   instead).
+
+ * The development community has adopted Contributor Covenant v2.0 to
+   update from v1.4 that we have been using.
+
+ * The support for deprecated PCRE1 library has been dropped.
+
+
+UI, Workflows & Features
+
+ * The "--format=%(trailers)" mechanism gets enhanced to make it
+   easier to design output for machine consumption.
+
+ * When a user does not tell "git pull" to use rebase or merge, the
+   command gives a loud message telling a user to choose between
+   rebase or merge but creates a merge anyway, forcing users who would
+   want to rebase to redo the operation.  Fix an early part of this
+   problem by tightening the condition to give the message---there is
+   no reason to stop or force the user to choose between rebase or
+   merge if the history fast-forwards.
+
+ * The configuration variable 'core.abbrev' can be set to 'no' to
+   force no abbreviation regardless of the hash algorithm.
+
+ * "git rev-parse" can be explicitly told to give output as absolute
+   or relative path with the `--path-format=(absolute|relative)` option.
+
+ * Bash completion (in contrib/) update to make it easier for
+   end-users to add completion for their custom "git" subcommands.
+
+ * "git maintenance" learned to drive scheduled maintenance on
+   platforms whose native scheduling methods are not 'cron'.
+
+ * After expiring a reflog and making a single commit, the reflog for
+   the branch would record a single entry that knows both @{0} and
+   @{1}, but we failed to answer "what commit were we on?", i.e. @{1}
+
+ * "git bundle" learns "--stdin" option to read its refs from the
+   standard input.  Also, it now does not lose refs whey they point
+   at the same object.
+
+ * "git log" learned a new "--diff-merges=<how>" option.
+
+ * "git ls-files" can and does show multiple entries when the index is
+   unmerged, which is a source for confusion unless -s/-u option is in
+   use.  A new option --deduplicate has been introduced.
+
+ * `git worktree list` now annotates worktrees as prunable, shows
+   locked and prunable attributes in --porcelain mode, and gained
+   a --verbose option.
+
+ * "git clone" tries to locally check out the branch pointed at by
+   HEAD of the remote repository after it is done, but the protocol
+   did not convey the information necessary to do so when copying an
+   empty repository.  The protocol v2 learned how to do so.
+
+ * There are other ways than ".." for a single token to denote a
+   "commit range", namely "<rev>^!" and "<rev>^-<n>", but "git
+   range-diff" did not understand them.
+
+ * The "git range-diff" command learned "--(left|right)-only" option
+   to show only one side of the compared range.
+
+ * "git mergetool" feeds three versions (base, local and remote) of
+   a conflicted path unmodified.  The command learned to optionally
+   prepare these files with unconflicted parts already resolved.
+
+ * The .mailmap is documented to be read only from the root level of a
+   working tree, but a stray file in a bare repository also was read
+   by accident, which has been corrected.
+
+ * "git maintenance" tool learned a new "pack-refs" maintenance task.
+
+ * The error message given when a configuration variable that is
+   expected to have a boolean value has been improved.
+
+ * Signed commits and tags now allow verification of objects, whose
+   two object names (one in SHA-1, the other in SHA-256) are both
+   signed.
+
+ * "git rev-list" command learned "--disk-usage" option.
+
+ * "git {diff,log} --{skip,rotate}-to=<path>" allows the user to
+   discard diff output for early paths or move them to the end of the
+   output.
+
+ * "git difftool" learned "--skip-to=<path>" option to restart an
+   interrupted session from an arbitrary path.
+
+ * "git grep" has been tweaked to be limited to the sparse checkout
+   paths.
+
+ * "git rebase --[no-]fork-point" gained a configuration variable
+   rebase.forkPoint so that users do not have to keep specifying a
+   non-default setting.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * A 3-year old test that was not testing anything useful has been
+   corrected.
+
+ * Retire more names with "sha1" in it.
+
+ * The topological walk codepath is covered by new trace2 stats.
+
+ * Update the Code-of-conduct to version 2.0 from the upstream (we've
+   been using version 1.4).
+
+ * "git mktag" validates its input using its own rules before writing
+   a tag object---it has been updated to share the logic with "git
+   fsck".
+
+ * Two new ways to feed configuration variable-value pairs via
+   environment variables have been introduced, and the way
+   GIT_CONFIG_PARAMETERS encodes variable/value pairs has been tweaked
+   to make it more robust.
+
+ * Tests have been updated so that they do not to get affected by the
+   name of the default branch "git init" creates.
+
+ * "git fetch" learns to treat ref updates atomically in all-or-none
+   fashion, just like "git push" does, with the new "--atomic" option.
+
+ * The peel_ref() API has been replaced with peel_iterated_oid().
+
+ * The .use_shell flag in struct child_process that is passed to
+   run_command() API has been clarified with a bit more documentation.
+
+ * Document, clean-up and optimize the code around the cache-tree
+   extension in the index.
+
+ * The ls-refs protocol operation has been optimized to narrow the
+   sub-hierarchy of refs/ it walks to produce response.
+
+ * When removing many branches and tags, the code used to do so one
+   ref at a time.  There is another API it can use to delete multiple
+   refs, and it makes quite a lot of performance difference when the
+   refs are packed.
+
+ * The "pack-objects" command needs to iterate over all the tags when
+   automatic tag following is enabled, but it actually iterated over
+   all refs and then discarded everything outside "refs/tags/"
+   hierarchy, which was quite wasteful.
+
+ * A perf script was made more portable.
+
+ * Our setting of GitHub CI test jobs were a bit too eager to give up
+   once there is even one failure found.  Tweak the knob to allow
+   other jobs keep running even when we see a failure, so that we can
+   find more failures in a single run.
+
+ * We've carried compatibility codepaths for compilers without
+   variadic macros for quite some time, but the world may be ready for
+   them to be removed.  Force compilation failure on exotic platforms
+   where variadic macros are not available to find out who screams in
+   such a way that we can easily revert if it turns out that the world
+   is not yet ready.
+
+ * Code clean-up to ensure our use of hashtables using object names as
+   keys use the "struct object_id" objects, not the raw hash values.
+
+ * Lose the debugging aid that may have been useful in the past, but
+   no longer is, in the "grep" codepaths.
+
+ * Some pretty-format specifiers do not need the data in commit object
+   (e.g. "%H"), but we were over-eager to load and parse it, which has
+   been made even lazier.
+
+ * Get rid of "GETTEXT_POISON" support altogether, which may or may
+   not be controversial.
+
+ * Introduce an on-disk file to record revindex for packdata, which
+   traditionally was always created on the fly and only in-core.
+
+ * The commit-graph learned to use corrected commit dates instead of
+   the generation number to help topological revision traversal.
+
+ * Piecemeal of rewrite of "git bisect" in C continues.
+
+ * When a pager spawned by us exited, the trace log did not record its
+   exit status correctly, which has been corrected.
+
+ * Removal of GIT_TEST_GETTEXT_POISON continues.
+
+ * The code to implement "git merge-base --independent" was poorly
+   done and was kept from the very beginning of the feature.
+
+ * Preliminary changes to fsmonitor integration.
+
+ * Performance improvements for rename detection.
+
+ * The common code to deal with "chunked file format" that is shared
+   by the multi-pack-index and commit-graph files have been factored
+   out, to help codepaths for both filetypes to become more robust.
+
+ * The approach to "fsck" the incoming objects in "index-pack" is
+   attractive for performance reasons (we have them already in core,
+   inflated and ready to be inspected), but fundamentally cannot be
+   applied fully when we receive more than one pack stream, as a tree
+   object in one pack may refer to a blob object in another pack as
+   ".gitmodules", when we want to inspect blobs that are used as
+   ".gitmodules" file, for example.  Teach "index-pack" to emit
+   objects that must be inspected later and check them in the calling
+   "fetch-pack" process.
+
+ * The logic to handle "trailer" related placeholders in the
+   "--format=" mechanisms in the "log" family and "for-each-ref"
+   family is getting unified.
+
+ * Raise the buffer size used when writing the index file out from
+   (obviously too small) 8kB to (clearly sufficiently large) 128kB.
+
+ * It is reported that open() on some platforms (e.g. macOS Big Sur)
+   can return EINTR even though our timers are set up with SA_RESTART.
+   A workaround has been implemented and enabled for macOS to rerun
+   open() transparently from the caller when this happens.
+
+
+Fixes since v2.30
+-----------------
+
+ * Diagnose command line error of "git rebase" early.
+
+ * Clean up option descriptions in "git cmd --help".
+
+ * "git stash" did not work well in a sparsely checked out working
+   tree.
+
+ * Some tests expect that "ls -l" output has either '-' or 'x' for
+   group executable bit, but setgid bit can be inherited from parent
+   directory and make these fields 'S' or 's' instead, causing test
+   failures.
+
+ * "git for-each-repo --config=<var> <cmd>" should not run <cmd> for
+   any repository when the configuration variable <var> is not defined
+   even once.
+
+ * Fix 2.29 regression where "git mergetool --tool-help" fails to list
+   all the available tools.
+
+ * Fix for procedure to building CI test environment for mac.
+
+ * The implementation of "git branch --sort" wrt the detached HEAD
+   display has always been hacky, which has been cleaned up.
+
+ * Newline characters in the host and path part of git:// URL are
+   now forbidden.
+
+ * "git diff" showed a submodule working tree with untracked cruft as
+   "Submodule commit <objectname>-dirty", but a natural expectation is
+   that the "-dirty" indicator would align with "git describe --dirty",
+   which does not consider having untracked files in the working tree
+   as source of dirtiness.  The inconsistency has been fixed.
+
+ * When more than one commit with the same patch ID appears on one
+   side, "git log --cherry-pick A...B" did not exclude them all when a
+   commit with the same patch ID appears on the other side.  Now it
+   does.
+
+ * Documentation for "git fsck" lost stale bits that has become
+   incorrect.
+
+ * Doc fix for packfile URI feature.
+
+ * When "git rebase -i" processes "fixup" insn, there is no reason to
+   clean up the commit log message, but we did the usual stripspace
+   processing.  This has been corrected.
+   (merge f7d42ceec5 js/rebase-i-commit-cleanup-fix later to maint).
+
+ * Fix in passing custom args from "git clone" to "upload-pack" on the
+   other side.
+   (merge ad6b5fefbd jv/upload-pack-filter-spec-quotefix later to maint).
+
+ * The command line completion (in contrib/) completed "git branch -d"
+   with branch names, but "git branch -D" offered tagnames in addition,
+   which has been corrected.  "git branch -M" had the same problem.
+   (merge 27dc071b9a jk/complete-branch-force-delete later to maint).
+
+ * When commands are started from a subdirectory, they may have to
+   compare the path to the subdirectory (called prefix and found out
+   from $(pwd)) with the tracked paths.  On macOS, $(pwd) and
+   readdir() yield decomposed path, while the tracked paths are
+   usually normalized to the precomposed form, causing mismatch.  This
+   has been fixed by taking the same approach used to normalize the
+   command line arguments.
+   (merge 5c327502db tb/precompose-prefix-too later to maint).
+
+ * Even though invocations of "die()" were logged to the trace2
+   system, "BUG()"s were not, which has been corrected.
+   (merge 0a9dde4a04 jt/trace2-BUG later to maint).
+
+ * "git grep --untracked" is meant to be "let's ALSO find in these
+   files on the filesystem" when looking for matches in the working
+   tree files, and does not make any sense if the primary search is
+   done against the index, or the tree objects.  The "--cached" and
+   "--untracked" options have been marked as mutually incompatible.
+   (merge 0c5d83b248 mt/grep-cached-untracked later to maint).
+
+ * Fix "git fsck --name-objects" which apparently has not been used by
+   anybody who is motivated enough to report breakage.
+   (merge e89f89361c js/fsck-name-objects-fix later to maint).
+
+ * Avoid individual tests in t5411 from getting affected by each other
+   by forcing them to use separate output files during the test.
+   (merge 822ee894f6 jx/t5411-unique-filenames later to maint).
+
+ * Test to make sure "git rev-parse one-thing one-thing" gives
+   the same thing twice (when one-thing is --since=X).
+   (merge a5cdca4520 ew/rev-parse-since-test later to maint).
+
+ * When certain features (e.g. grafts) used in the repository are
+   incompatible with the use of the commit-graph, we used to silently
+   turned commit-graph off; we now tell the user what we are doing.
+   (merge c85eec7fc3 js/commit-graph-warning later to maint).
+
+ * Objects that lost references can be pruned away, even when they
+   have notes attached to it (and these notes will become dangling,
+   which in turn can be pruned with "git notes prune").  This has been
+   clarified in the documentation.
+   (merge fa9ab027ba mz/doc-notes-are-not-anchors later to maint).
+
+ * The error codepath around the "--temp/--prefix" feature of "git
+   checkout-index" has been improved.
+   (merge 3f7ba60350 mt/checkout-index-corner-cases later to maint).
+
+ * The "git maintenance register" command had trouble registering bare
+   repositories, which had been corrected.
+
+ * A handful of multi-word configuration variable names in
+   documentation that are spelled in all lowercase have been corrected
+   to use the more canonical camelCase.
+   (merge 7dd0eaa39c dl/doc-config-camelcase later to maint).
+
+ * "git push $there --delete ''" should have been diagnosed as an
+   error, but instead turned into a matching push, which has been
+   corrected.
+   (merge 20e416409f jc/push-delete-nothing later to maint).
+
+ * Test script modernization.
+   (merge 488acf15df sv/t7001-modernize later to maint).
+
+ * An under-allocation for the untracked cache data has been corrected.
+   (merge 6347d649bc jh/untracked-cache-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge e3f5da7e60 sg/t7800-difftool-robustify later to maint).
+   (merge 9d336655ba js/doc-proto-v2-response-end later to maint).
+   (merge 1b5b8cf072 jc/maint-column-doc-typofix later to maint).
+   (merge 3a837b58e3 cw/pack-config-doc later to maint).
+   (merge 01168a9d89 ug/doc-commit-approxidate later to maint).
+   (merge b865734760 js/params-vs-args later to maint).
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index dc3bceb..117f4cf 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -1,6 +1,6 @@
 -b::
 	Show blank SHA-1 for boundary commits.  This can also
-	be controlled via the `blame.blankboundary` config option.
+	be controlled via the `blame.blankBoundary` config option.
 
 --root::
 	Do not treat root commits as boundaries.  This can also be
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 6ba50b1..d08e83a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -398,6 +398,8 @@
 
 include::config/log.txt[]
 
+include::config/lsrefs.txt[]
+
 include::config/mailinfo.txt[]
 
 include::config/mailmap.txt[]
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 160aaca..c04f62a 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -625,4 +625,6 @@
 	computed based on the approximate number of packed objects
 	in your repository, which hopefully is enough for
 	abbreviated object names to stay unique for some time.
+	If set to "no", no abbreviation is made and the object names
+	are shown in their full length.
 	The minimum length is 4.
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index c3ae136..2d3331f 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -85,6 +85,8 @@
 	and 'git status' when `status.submoduleSummary` is set unless it is
 	overridden by using the --ignore-submodules command-line option.
 	The 'git submodule' commands are not affected by this setting.
+	By default this is set to untracked so that any untracked
+	submodules are ignored.
 
 diff.mnemonicPrefix::
 	If set, 'git diff' uses a prefix pair that is different from the
diff --git a/Documentation/config/init.txt b/Documentation/config/init.txt
index dc77f8c..79c79d6 100644
--- a/Documentation/config/init.txt
+++ b/Documentation/config/init.txt
@@ -4,4 +4,4 @@
 
 init.defaultBranch::
 	Allows overriding the default branch name e.g. when initializing
-	a new repository or when cloning an empty repository.
+	a new repository.
diff --git a/Documentation/config/lsrefs.txt b/Documentation/config/lsrefs.txt
new file mode 100644
index 0000000..adeda0f
--- /dev/null
+++ b/Documentation/config/lsrefs.txt
@@ -0,0 +1,9 @@
+lsrefs.unborn::
+	May be "advertise" (the default), "allow", or "ignore". If "advertise",
+	the server will respond to the client sending "unborn" (as described in
+	protocol-v2.txt) and will advertise support for this feature during the
+	protocol v2 capability advertisement. "allow" is the same as
+	"advertise" except that the server will not advertise support for this
+	feature; this is useful for load-balanced servers that cannot be
+	updated atomically (for example), since the administrator could
+	configure "allow", then after a delay, configure "advertise".
diff --git a/Documentation/config/maintenance.txt b/Documentation/config/maintenance.txt
index a5ead09..18f0562 100644
--- a/Documentation/config/maintenance.txt
+++ b/Documentation/config/maintenance.txt
@@ -15,8 +15,9 @@
 * `none`: This default setting implies no task are run at any schedule.
 * `incremental`: This setting optimizes for performing small maintenance
   activities that do not delete any data. This does not schedule the `gc`
-  task, but runs the `prefetch` and `commit-graph` tasks hourly and the
-  `loose-objects` and `incremental-repack` tasks daily.
+  task, but runs the `prefetch` and `commit-graph` tasks hourly, the
+  `loose-objects` and `incremental-repack` tasks daily, and the `pack-refs`
+  task weekly.
 
 maintenance.<task>.enabled::
 	This boolean config option controls whether the maintenance task
diff --git a/Documentation/config/mergetool.txt b/Documentation/config/mergetool.txt
index 16a2744..90f76f5 100644
--- a/Documentation/config/mergetool.txt
+++ b/Documentation/config/mergetool.txt
@@ -13,6 +13,11 @@
 	merged; 'MERGED' contains the name of the file to which the merge
 	tool should write the results of a successful merge.
 
+mergetool.<tool>.hideResolved::
+	Allows the user to override the global `mergetool.hideResolved` value
+	for a specific tool. See `mergetool.hideResolved` for the full
+	description.
+
 mergetool.<tool>.trustExitCode::
 	For a custom merge command, specify whether the exit code of
 	the merge command can be used to determine whether the merge was
@@ -40,6 +45,16 @@
 	value of `false` avoids using `--auto-merge` altogether, and is the
 	default value.
 
+mergetool.hideResolved::
+	During a merge Git will automatically resolve as many conflicts as
+	possible and write the 'MERGED' file containing conflict markers around
+	any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
+	represent the versions of the file from before Git's conflict
+	resolution. This flag causes 'LOCAL' and 'REMOTE' to be overwriten so
+	that only the unresolved conflicts are presented to the merge tool. Can
+	be configured per-tool via the `mergetool.<tool>.hideResolved`
+	configuration variable. Defaults to `true`.
+
 mergetool.keepBackup::
 	After performing a merge, the original file with conflict markers
 	can be saved as a file with a `.orig` extension.  If this variable
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 837f1b1..3da4ea9 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -133,3 +133,10 @@
 	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. Defaults to true.
+
+pack.writeReverseIndex::
+	When true, git will write a corresponding .rev file (see:
+	link:../technical/pack-format.html[Documentation/technical/pack-format.txt])
+	for each new packfile that it writes in all places except for
+	linkgit:git-fast-import[1] and in the bulk checkin mechanism.
+	Defaults to false.
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index 7f7a07d..214f31b 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -68,3 +68,6 @@
 	Automatically reschedule `exec` commands that failed. This only makes
 	sense in interactive mode (or when an `--exec` option was provided).
 	This is the same as specifying the `--reschedule-failed-exec` option.
+
+rebase.forkPoint::
+	If set to false set `--no-fork-point` option by default.
diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
index f1097fa..99c455f 100644
--- a/Documentation/date-formats.txt
+++ b/Documentation/date-formats.txt
@@ -1,10 +1,7 @@
 DATE FORMATS
 ------------
 
-The `GIT_AUTHOR_DATE`, `GIT_COMMITTER_DATE` environment variables
-ifdef::git-commit[]
-and the `--date` option
-endif::git-commit[]
+The `GIT_AUTHOR_DATE` and `GIT_COMMITTER_DATE` environment variables
 support the following date formats:
 
 Git internal format::
@@ -26,3 +23,9 @@
 +
 NOTE: In addition, the date part is accepted in the following formats:
 `YYYY.MM.DD`, `MM/DD/YYYY` and `DD.MM.YYYY`.
+
+ifdef::git-commit[]
+In addition to recognizing all date formats above, the `--date` option
+will also try to make sense of other, more human-centric date formats,
+such as relative dates like "yesterday" or "last Friday at noon".
+endif::git-commit[]
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index b10ff4c..2db8eac 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -81,9 +81,9 @@
 Any diff-generating command can take the `-c` or `--cc` option to
 produce a 'combined diff' when showing a merge. This is the default
 format when showing merges with linkgit:git-diff[1] or
-linkgit:git-show[1]. Note also that you can give the `-m` option to any
-of these commands to force generation of diffs with individual parents
-of a merge.
+linkgit:git-show[1]. Note also that you can give suitable
+`--diff-merges` option to any of these commands to force generation of
+diffs in specific format.
 
 A "combined diff" format looks like this:
 
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 746b144..aa2b5c1 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -33,6 +33,57 @@
 	show the patch by default, or to cancel the effect of `--patch`.
 endif::git-format-patch[]
 
+ifdef::git-log[]
+--diff-merges=(off|none|first-parent|1|separate|m|combined|c|dense-combined|cc)::
+--no-diff-merges::
+	Specify diff format to be used for merge commits. Default is
+	{diff-merges-default} unless `--first-parent` is in use, in which case
+	`first-parent` is the default.
++
+--diff-merges=(off|none):::
+--no-diff-merges:::
+	Disable output of diffs for merge commits. Useful to override
+	implied value.
++
+--diff-merges=first-parent:::
+--diff-merges=1:::
+	This option makes merge commits show the full diff with
+	respect to the first parent only.
++
+--diff-merges=separate:::
+--diff-merges=m:::
+-m:::
+	This makes merge commits show the full diff with respect to
+	each of the parents. Separate log entry and diff is generated
+	for each parent. `-m` doesn't produce any output without `-p`.
++
+--diff-merges=combined:::
+--diff-merges=c:::
+-c:::
+	With this option, diff output for a merge commit shows the
+	differences from each of the parents to the merge result
+	simultaneously instead of showing pairwise diff between a
+	parent and the result one at a time. Furthermore, it lists
+	only files which were modified from all parents. `-c` implies
+	`-p`.
++
+--diff-merges=dense-combined:::
+--diff-merges=cc:::
+--cc:::
+	With this option the output produced by
+	`--diff-merges=combined` is further compressed by omitting
+	uninteresting hunks whose contents in the parents have only
+	two variants and the merge result picks one of them without
+	modification.  `--cc` implies `-p`.
+
+--combined-all-paths::
+	This flag causes combined diffs (used for merge commits) to
+	list the name of the file from all parents.  It thus only has
+	effect when `--diff-merges=[dense-]combined` is in use, and
+	is likely only useful if filename changes are detected (i.e.
+	when either rename or copy detection have been requested).
+endif::git-log[]
+
 -U<n>::
 --unified=<n>::
 	Generate diffs with <n> lines of context instead of
@@ -649,6 +700,14 @@
 components matches the pattern.  For example, the pattern "`foo*bar`"
 matches "`fooasdfbar`" and "`foo/bar/baz/asdf`" but not "`foobarx`".
 
+--skip-to=<file>::
+--rotate-to=<file>::
+	Discard the files before the named <file> from the output
+	(i.e. 'skip to'), or move them to the end of the output
+	(i.e. 'rotate to').  These were invented primarily for use
+	of the `git difftool` command, and may not be very useful
+	otherwise.
+
 ifndef::git-format-patch[]
 -R::
 	Swap two inputs; that is, show differences from index or
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 2bf77b4..07783de 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -7,6 +7,10 @@
 	existing contents of `.git/FETCH_HEAD`.  Without this
 	option old data in `.git/FETCH_HEAD` will be overwritten.
 
+--atomic::
+	Use an atomic transaction to update local refs. Either all refs are
+	updated, or on error, no refs are updated.
+
 --depth=<depth>::
 	Limit fetching to the specified number of commits from the tip of
 	each remote branch history. If fetching to a 'shallow' repository
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 06bc063..decd8ae 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -79,7 +79,7 @@
 	Pass `-u` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 	The proposed commit log message taken from the e-mail
 	is re-coded into UTF-8 encoding (configuration variable
-	`i18n.commitencoding` can be used to specify project's
+	`i18n.commitEncoding` can be used to specify project's
 	preferred encoding if it is not UTF-8).
 +
 This was optional in prior versions of git, but now it is the
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 34b496d..3bf5d5d 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -226,7 +226,7 @@
 MAPPING AUTHORS
 ---------------
 
-include::mailmap.txt[]
+See linkgit:gitmailmap[5].
 
 
 SEE ALSO
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index adaa178..94dc9a5 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -78,8 +78,8 @@
 to happen.
 
 The `-c` and `-C` options have the exact same semantics as `-m` and
-`-M`, except instead of the branch being renamed it along with its
-config and reflog will be copied to a new name.
+`-M`, except instead of the branch being renamed, it will be copied to a
+new name, along with its config and reflog.
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
@@ -153,7 +153,7 @@
 --column[=<options>]::
 --no-column::
 	Display branch listing in columns. See configuration variable
-	column.branch for option syntax.`--column` and `--no-column`
+	`column.branch` for option syntax. `--column` and `--no-column`
 	without options are equivalent to 'always' and 'never' respectively.
 +
 This option is only applicable in non-verbose mode.
diff --git a/Documentation/git-check-mailmap.txt b/Documentation/git-check-mailmap.txt
index aa2055d..02f4418 100644
--- a/Documentation/git-check-mailmap.txt
+++ b/Documentation/git-check-mailmap.txt
@@ -36,10 +36,17 @@
 printed; otherwise only ``$$<user@host>$$'' is printed.
 
 
+CONFIGURATION
+-------------
+
+See `mailmap.file` and `mailmap.blob` in linkgit:git-config[1] for how
+to specify a custom `.mailmap` target file or object.
+
+
 MAPPING AUTHORS
 ---------------
 
-include::mailmap.txt[]
+See linkgit:gitmailmap[5].
 
 
 GIT
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 0e9351d..4b4cc5c 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -346,6 +346,22 @@
 
 See also <<FILES>>.
 
+GIT_CONFIG_COUNT::
+GIT_CONFIG_KEY_<n>::
+GIT_CONFIG_VALUE_<n>::
+	If GIT_CONFIG_COUNT is set to a positive number, all environment pairs
+	GIT_CONFIG_KEY_<n> and GIT_CONFIG_VALUE_<n> up to that number will be
+	added to the process's runtime configuration. The config pairs are
+	zero-indexed. Any missing key or value is treated as an error. An empty
+	GIT_CONFIG_COUNT is treated the same as GIT_CONFIG_COUNT=0, namely no
+	pairs are processed. These environment variables will override values
+	in configuration files, but will be overridden by any explicit options
+	passed via `git -c`.
++
+This is useful for cases where you want to spawn multiple git commands
+with a common configuration but cannot depend on a configuration file,
+for example when writing scripts.
+
 
 [[EXAMPLES]]
 EXAMPLES
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index 484c485..143b0c4 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -34,6 +34,14 @@
 	This is the default behaviour; the option is provided to
 	override any configuration settings.
 
+--rotate-to=<file>::
+	Start showing the diff for the given path,
+	the paths before it will move to end and output.
+
+--skip-to=<file>::
+	Start showing the diff for the given path, skipping all
+	the paths before it.
+
 -t <tool>::
 --tool=<tool>::
 	Use the diff tool specified by <tool>.  Valid values include
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 2962f85..2ae2478 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -260,11 +260,9 @@
 	The first `N` lines of the message.
 
 Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]
-are obtained as `trailers` (or by using the historical alias
-`contents:trailers`).  Non-trailer lines from the trailer block can be omitted
-with `trailers:only`. Whitespace-continuations can be removed from trailers so
-that each trailer appears on a line by itself with its full content with
-`trailers:unfold`. Both can be used together as `trailers:unfold,only`.
+are obtained as `trailers[:options]` (or by using the historical alias
+`contents:trailers[:options]`). For valid [:option] values see `trailers`
+section of linkgit:git-log[1].
 
 For sorting purposes, fields with numeric values sort in numeric order
 (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`).
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 0c114ad..853967d 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -117,12 +117,14 @@
 'git gc' tries very hard not to delete objects that are referenced
 anywhere in your repository. In particular, it will keep not only
 objects referenced by your current set of branches and tags, but also
-objects referenced by the index, remote-tracking branches, notes saved
-by 'git notes' under refs/notes/, reflogs (which may reference commits
-in branches that were later amended or rewound), and anything else in
-the refs/* namespace.  If you are expecting some objects to be deleted
-and they aren't, check all of those locations and decide whether it
-makes sense in your case to remove those references.
+objects referenced by the index, remote-tracking branches, reflogs
+(which may reference commits in branches that were later amended or
+rewound), and anything else in the refs/* namespace. Note that a note
+(of the kind created by 'git notes') attached to an object does not
+contribute in keeping the object alive. If you are expecting some
+objects to be deleted and they aren't, check all of those locations
+and decide whether it makes sense in your case to remove those
+references.
 
 On the other hand, when 'git gc' runs concurrently with another process,
 there is a risk of it deleting an object that the other process is using
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index 4deb489..9fa17b6 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -41,11 +41,17 @@
 		<commit-id>['\t'<filename-as-in--w>]
 
 --packfile=<hash>::
-	Instead of a commit id on the command line (which is not expected in
+	For internal use only. Instead of a commit id on the command
+	line (which is not expected in
 	this case), 'git http-fetch' fetches the packfile directly at the given
 	URL and uses index-pack to generate corresponding .idx and .keep files.
 	The hash is used to determine the name of the temporary file and is
-	arbitrary. The output of index-pack is printed to stdout.
+	arbitrary. The output of index-pack is printed to stdout. Requires
+	--index-pack-args.
+
+--index-pack-args=<args>::
+	For internal use only. The command to run on the contents of the
+	downloaded pack. Arguments are URL-encoded separated by spaces.
 
 --recover::
 	Verify that everything reachable from target is fetched.  Used after
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index af0c262..7fa74b9 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -9,17 +9,18 @@
 SYNOPSIS
 --------
 [verse]
-'git index-pack' [-v] [-o <index-file>] <pack-file>
+'git index-pack' [-v] [-o <index-file>] [--[no-]rev-index] <pack-file>
 'git index-pack' --stdin [--fix-thin] [--keep] [-v] [-o <index-file>]
-                 [<pack-file>]
+		  [--[no-]rev-index] [<pack-file>]
 
 
 DESCRIPTION
 -----------
 Reads a packed archive (.pack) from the specified file, and
-builds a pack index file (.idx) for it.  The packed archive
-together with the pack index can then be placed in the
-objects/pack/ directory of a Git repository.
+builds a pack index file (.idx) for it. Optionally writes a
+reverse-index (.rev) for the specified pack. The packed
+archive together with the pack index can then be placed in
+the objects/pack/ directory of a Git repository.
 
 
 OPTIONS
@@ -35,6 +36,13 @@
 	fails if the name of packed archive does not end
 	with .pack).
 
+--[no-]rev-index::
+	When this flag is provided, generate a reverse index
+	(a `.rev` file) corresponding to the given pack. If
+	`--verify` is given, ensure that the existing
+	reverse index is correct. Takes precedence over
+	`pack.writeReverseIndex`.
+
 --stdin::
 	When this flag is provided, the pack is read from stdin
 	instead and a copy is then written to <pack-file>. If
@@ -78,7 +86,12 @@
 	Die if the pack contains broken links. For internal use only.
 
 --fsck-objects::
-	Die if the pack contains broken objects. For internal use only.
+	For internal use only.
++
+Die if the pack contains broken objects. If the pack contains a tree
+pointing to a .gitmodules blob that does not exist, prints the hash of
+that blob (for the caller to check) after the hash that goes into the
+name of the pack/idx file (see "Notes").
 
 --threads=<n>::
 	Specifies the number of threads to spawn when resolving
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index dd189a3..1bbf865 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -107,47 +107,15 @@
 By default, `git log` does not generate any diff output. The options
 below can be used to show the changes made by each commit.
 
-Note that unless one of `-c`, `--cc`, or `-m` is given, merge commits
-will never show a diff, even if a diff format like `--patch` is
-selected, nor will they match search options like `-S`. The exception is
-when `--first-parent` is in use, in which merges are treated like normal
-single-parent commits (this can be overridden by providing a
-combined-diff option or with `--no-diff-merges`).
-
--c::
-	With this option, diff output for a merge commit
-	shows the differences from each of the parents to the merge result
-	simultaneously instead of showing pairwise diff between a parent
-	and the result one at a time. Furthermore, it lists only files
-	which were modified from all parents.
-
---cc::
-	This flag implies the `-c` option and further compresses the
-	patch output by omitting uninteresting hunks whose contents in
-	the parents have only two variants and the merge result picks
-	one of them without modification.
-
---combined-all-paths::
-	This flag causes combined diffs (used for merge commits) to
-	list the name of the file from all parents.  It thus only has
-	effect when -c or --cc are specified, and is likely only
-	useful if filename changes are detected (i.e. when either
-	rename or copy detection have been requested).
-
--m::
-	This flag makes the merge commits show the full diff like
-	regular commits; for each merge parent, a separate log entry
-	and diff is generated. An exception is that only diff against
-	the first parent is shown when `--first-parent` option is given;
-	in that case, the output represents the changes the merge
-	brought _into_ the then-current branch.
-
---diff-merges=off::
---no-diff-merges::
-	Disable output of diffs for merge commits (default). Useful to
-	override `-m`, `-c`, or `--cc`.
+Note that unless one of `--diff-merges` variants (including short
+`-m`, `-c`, and `--cc` options) is explicitly given, merge commits
+will not show a diff, even if a diff format like `--patch` is
+selected, nor will they match search options like `-S`. The exception
+is when `--first-parent` is in use, in which case `first-parent` is
+the default format.
 
 :git-log: 1
+:diff-merges-default: `off`
 include::diff-options.txt[]
 
 include::diff-generate-patch.txt[]
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 0a3b526..6d11ab5 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -13,6 +13,7 @@
 		(--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
 		(-[c|d|o|i|s|u|k|m])*
 		[--eol]
+		[--deduplicate]
 		[-x <pattern>|--exclude=<pattern>]
 		[-X <file>|--exclude-from=<file>]
 		[--exclude-per-directory=<file>]
@@ -80,6 +81,13 @@
 	\0 line termination on output and do not quote filenames.
 	See OUTPUT below for more information.
 
+--deduplicate::
+	When only filenames are shown, suppress duplicates that may
+	come from having multiple stages during a merge, or giving
+	`--deleted` and `--modified` option at the same time.
+	When any of the `-t`, `--unmerged`, or `--stage` option is
+	in use, this option has no effect.
+
 -x <pattern>::
 --exclude=<pattern>::
 	Skip untracked files matching pattern.
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 7a6aed0..d343f04 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -53,7 +53,7 @@
 	The commit log message, author name and author email are
 	taken from the e-mail, and after minimally decoding MIME
 	transfer encoding, re-coded in the charset specified by
-	i18n.commitencoding (defaulting to UTF-8) by transliterating
+	`i18n.commitEncoding` (defaulting to UTF-8) by transliterating
 	them.  This used to be optional but now it is the default.
 +
 Note that the patch is always used as-is without charset
@@ -61,7 +61,7 @@
 
 --encoding=<encoding>::
 	Similar to -u.  But when re-coding, the charset specified here is
-	used instead of the one specified by i18n.commitencoding or UTF-8.
+	used instead of the one specified by `i18n.commitEncoding` or UTF-8.
 
 -n::
 	Disable all charset re-coding of the metadata.
diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt
index 6fe1e5e..80ddd33 100644
--- a/Documentation/git-maintenance.txt
+++ b/Documentation/git-maintenance.txt
@@ -145,6 +145,12 @@
 	which is a special case that attempts to repack all pack-files
 	into a single pack-file.
 
+pack-refs::
+	The `pack-refs` task collects the loose reference files and
+	collects them into a single file. This speeds up operations that
+	need to iterate across many references. See linkgit:git-pack-refs[1]
+	for more information.
+
 OPTIONS
 -------
 --auto::
@@ -218,6 +224,122 @@
 but does not take the lock in the same way as `git maintenance run`. If
 possible, use `git maintenance run --task=gc` instead of `git gc`.
 
+The following sections describe the mechanisms put in place to run
+background maintenance by `git maintenance start` and how to customize
+them.
+
+BACKGROUND MAINTENANCE ON POSIX SYSTEMS
+---------------------------------------
+
+The standard mechanism for scheduling background tasks on POSIX systems
+is cron(8). This tool executes commands based on a given schedule. The
+current list of user-scheduled tasks can be found by running `crontab -l`.
+The schedule written by `git maintenance start` is similar to this:
+
+-----------------------------------------------------------------------
+# BEGIN GIT MAINTENANCE SCHEDULE
+# The following schedule was created by Git
+# Any edits made in this region might be
+# replaced in the future by a Git command.
+
+0 1-23 * * * "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=hourly
+0 0 * * 1-6 "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=daily
+0 0 * * 0 "/<path>/git" --exec-path="/<path>" for-each-repo --config=maintenance.repo maintenance run --schedule=weekly
+
+# END GIT MAINTENANCE SCHEDULE
+-----------------------------------------------------------------------
+
+The comments are used as a region to mark the schedule as written by Git.
+Any modifications within this region will be completely deleted by
+`git maintenance stop` or overwritten by `git maintenance start`.
+
+The `crontab` entry specifies the full path of the `git` executable to
+ensure that the executed `git` command is the same one with which
+`git maintenance start` was issued independent of `PATH`. If the same user
+runs `git maintenance start` with multiple Git executables, then only the
+latest executable is used.
+
+These commands use `git for-each-repo --config=maintenance.repo` to run
+`git maintenance run --schedule=<frequency>` on each repository listed in
+the multi-valued `maintenance.repo` config option. These are typically
+loaded from the user-specific global config. The `git maintenance` process
+then determines which maintenance tasks are configured to run on each
+repository with each `<frequency>` using the `maintenance.<task>.schedule`
+config options. These values are loaded from the global or repository
+config values.
+
+If the config values are insufficient to achieve your desired background
+maintenance schedule, then you can create your own schedule. If you run
+`crontab -e`, then an editor will load with your user-specific `cron`
+schedule. In that editor, you can add your own schedule lines. You could
+start by adapting the default schedule listed earlier, or you could read
+the crontab(5) documentation for advanced scheduling techniques. Please
+do use the full path and `--exec-path` techniques from the default
+schedule to ensure you are executing the correct binaries in your
+schedule.
+
+
+BACKGROUND MAINTENANCE ON MACOS SYSTEMS
+---------------------------------------
+
+While macOS technically supports `cron`, using `crontab -e` requires
+elevated privileges and the executed process does not have a full user
+context. Without a full user context, Git and its credential helpers
+cannot access stored credentials, so some maintenance tasks are not
+functional.
+
+Instead, `git maintenance start` interacts with the `launchctl` tool,
+which is the recommended way to schedule timed jobs in macOS. Scheduling
+maintenance through `git maintenance (start|stop)` requires some
+`launchctl` features available only in macOS 10.11 or later.
+
+Your user-specific scheduled tasks are stored as XML-formatted `.plist`
+files in `~/Library/LaunchAgents/`. You can see the currently-registered
+tasks using the following command:
+
+-----------------------------------------------------------------------
+$ ls ~/Library/LaunchAgents/org.git-scm.git*
+org.git-scm.git.daily.plist
+org.git-scm.git.hourly.plist
+org.git-scm.git.weekly.plist
+-----------------------------------------------------------------------
+
+One task is registered for each `--schedule=<frequency>` option. To
+inspect how the XML format describes each schedule, open one of these
+`.plist` files in an editor and inspect the `<array>` element following
+the `<key>StartCalendarInterval</key>` element.
+
+`git maintenance start` will overwrite these files and register the
+tasks again with `launchctl`, so any customizations should be done by
+creating your own `.plist` files with distinct names. Similarly, the
+`git maintenance stop` command will unregister the tasks with `launchctl`
+and delete the `.plist` files.
+
+To create more advanced customizations to your background tasks, see
+launchctl.plist(5) for more information.
+
+
+BACKGROUND MAINTENANCE ON WINDOWS SYSTEMS
+-----------------------------------------
+
+Windows does not support `cron` and instead has its own system for
+scheduling background tasks. The `git maintenance start` command uses
+the `schtasks` command to submit tasks to this system. You can inspect
+all background tasks using the Task Scheduler application. The tasks
+added by Git have names of the form `Git Maintenance (<frequency>)`.
+The Task Scheduler GUI has ways to inspect these tasks, but you can also
+export the tasks to XML files and view the details there.
+
+Note that since Git is a console application, these background tasks
+create a console window visible to the current user. This can be changed
+manually by selecting the "Run whether user is logged in or not" option
+in Task Scheduler. This change requires a password input, which is why
+`git maintenance start` does not select it by default.
+
+If you want to customize the background tasks, please rename the tasks
+so future calls to `git maintenance (start|stop)` do not overwrite your
+custom tasks.
+
 
 GIT
 ---
diff --git a/Documentation/git-mergetool--lib.txt b/Documentation/git-mergetool--lib.txt
index 4da9d24..3e8f59a 100644
--- a/Documentation/git-mergetool--lib.txt
+++ b/Documentation/git-mergetool--lib.txt
@@ -38,6 +38,10 @@
 get_merge_tool_path::
 	returns the custom path for a merge tool.
 
+initialize_merge_tool::
+	bring merge tool specific functions into scope so they can be used or
+	overridden.
+
 run_merge_tool::
 	launches a merge tool given the tool name and a true/false
 	flag to indicate whether a merge base is present.
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index fa6a756..17a2603 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-mktag - Creates a tag object
+git-mktag - Creates a tag object with extra validation
 
 
 SYNOPSIS
@@ -11,25 +11,52 @@
 [verse]
 'git mktag'
 
+OPTIONS
+-------
+
+--strict::
+	By default mktag turns on the equivalent of
+	linkgit:git-fsck[1] `--strict` mode. Use `--no-strict` to
+	disable it.
+
 DESCRIPTION
 -----------
-Reads a tag contents on standard input and creates a tag object
-that can also be used to sign other objects.
 
-The output is the new tag's <object> identifier.
+Reads a tag contents on standard input and creates a tag object. The
+output is the new tag's <object> identifier.
+
+This command is mostly equivalent to linkgit:git-hash-object[1]
+invoked with `-t tag -w --stdin`. I.e. both of these will create and
+write a tag found in `my-tag`:
+
+    git mktag <my-tag
+    git hash-object -t tag -w --stdin <my-tag
+
+The difference is that mktag will die before writing the tag if the
+tag doesn't pass a linkgit:git-fsck[1] check.
+
+The "fsck" check done mktag is stricter than what linkgit:git-fsck[1]
+would run by default in that all `fsck.<msg-id>` messages are promoted
+from warnings to errors (so e.g. a missing "tagger" line is an error).
+
+Extra headers in the object are also an error under mktag, but ignored
+by linkgit:git-fsck[1]. This extra check can be turned off by setting
+the appropriate `fsck.<msg-id>` varible:
+
+    git -c fsck.extraHeaderEntry=ignore mktag <my-tag-with-headers
 
 Tag Format
 ----------
 A tag signature file, to be fed to this command's standard input,
 has a very simple fixed format: four lines of
 
-  object <sha1>
+  object <hash>
   type <typename>
   tag <tagname>
   tagger <tagger>
 
 followed by some 'optional' free-form message (some tags created
-by older Git may not have `tagger` line).  The message, when
+by older Git may not have `tagger` line).  The message, when it
 exists, is separated by a blank line from the header.  The
 message part may contain a signature that Git itself doesn't
 care about, but that can be verified with gpg.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index 54d715e..f85cb7e 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -400,6 +400,17 @@
 one wins" ordering (which allows repo-specific config to take precedence
 over user-wide config, and so forth).
 
+
+CONFIGURATION
+-------------
+
+Various configuration variables affect packing, see
+linkgit:git-config[1] (search for "pack" and "delta").
+
+Notably, delta compression is not used on objects larger than the
+`core.bigFileThreshold` configuration variable and on files with the
+attribute `delta` set to false.
+
 SEE ALSO
 --------
 linkgit:git-rev-list[1]
diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
index 9701c1e..fe350d7 100644
--- a/Documentation/git-range-diff.txt
+++ b/Documentation/git-range-diff.txt
@@ -10,6 +10,7 @@
 [verse]
 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
 	[--no-dual-color] [--creation-factor=<factor>]
+	[--left-only | --right-only]
 	( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
 
 DESCRIPTION
@@ -28,6 +29,17 @@
 second commit range, with unmatched commits being inserted just after
 all of their ancestors have been shown.
 
+There are three ways to specify the commit ranges:
+
+- `<range1> <range2>`: Either commit range can be of the form
+  `<base>..<rev>`, `<rev>^!` or `<rev>^-<n>`. See `SPECIFYING RANGES`
+  in linkgit:gitrevisions[7] for more details.
+
+- `<rev1>...<rev2>`. This is equivalent to
+  `<rev2>..<rev1> <rev1>..<rev2>`.
+
+- `<base> <rev1> <rev2>`: This is equivalent to `<base>..<rev1>
+  <base>..<rev2>`.
 
 OPTIONS
 -------
@@ -57,6 +69,14 @@
 	See the ``Algorithm`` section below for an explanation why this is
 	needed.
 
+--left-only::
+	Suppress commits that are missing from the first specified range
+	(or the "left range" when using the `<rev1>...<rev2>` format).
+
+--right-only::
+	Suppress commits that are missing from the second specified range
+	(or the "right range" when using the `<rev1>...<rev2>` format).
+
 --[no-]notes[=<ref>]::
 	This flag is passed to the `git log` program
 	(see linkgit:git-log[1]) that generates the patches.
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 92f146d..fbd4b4a 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -165,9 +165,12 @@
 	Pass the `--delta-islands` option to `git-pack-objects`, see
 	linkgit:git-pack-objects[1].
 
-Configuration
+CONFIGURATION
 -------------
 
+Various configuration variables affect packing, see
+linkgit:git-config[1] (search for "pack" and "delta").
+
 By default, the command passes `--delta-base-offset` option to
 'git pack-objects'; this typically results in slightly smaller packs,
 but the generated packs are incompatible with versions of Git older than
@@ -178,6 +181,10 @@
 is unaffected by this option as the conversion is performed on the fly
 as needed in that case.
 
+Delta compression is not used on objects larger than the
+`core.bigFileThreshold` configuration variable and on files with the
+attribute `delta` set to false.
+
 SEE ALSO
 --------
 linkgit:git-pack-objects[1]
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 5da6623..20bb8e8 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -31,6 +31,99 @@
 
 include::pretty-formats.txt[]
 
+EXAMPLES
+--------
+
+* Print the list of commits reachable from the current branch.
++
+----------
+git rev-list HEAD
+----------
+
+* Print the list of commits on this branch, but not present in the
+  upstream branch.
++
+----------
+git rev-list @{upstream}..HEAD
+----------
+
+* Format commits with their author and commit message (see also the
+  porcelain linkgit:git-log[1]).
++
+----------
+git rev-list --format=medium HEAD
+----------
+
+* Format commits along with their diffs (see also the porcelain
+  linkgit:git-log[1], which can do this in a single process).
++
+----------
+git rev-list HEAD |
+git diff-tree --stdin --format=medium -p
+----------
+
+* Print the list of commits on the current branch that touched any
+  file in the `Documentation` directory.
++
+----------
+git rev-list HEAD -- Documentation/
+----------
+
+* Print the list of commits authored by you in the past year, on
+  any branch, tag, or other ref.
++
+----------
+git rev-list --author=you@example.com --since=1.year.ago --all
+----------
+
+* Print the list of objects reachable from the current branch (i.e., all
+  commits and the blobs and trees they contain).
++
+----------
+git rev-list --objects HEAD
+----------
+
+* Compare the disk size of all reachable objects, versus those
+  reachable from reflogs, versus the total packed size. This can tell
+  you whether running `git repack -ad` might reduce the repository size
+  (by dropping unreachable objects), and whether expiring reflogs might
+  help.
++
+----------
+# reachable objects
+git rev-list --disk-usage --objects --all
+# plus reflogs
+git rev-list --disk-usage --objects --all --reflog
+# total disk size used
+du -c .git/objects/pack/*.pack .git/objects/??/*
+# alternative to du: add up "size" and "size-pack" fields
+git count-objects -v
+----------
+
+* Report the disk size of each branch, not including objects used by the
+  current branch. This can find outliers that are contributing to a
+  bloated repository size (e.g., because somebody accidentally committed
+  large build artifacts).
++
+----------
+git for-each-ref --format='%(refname)' |
+while read branch
+do
+	size=$(git rev-list --disk-usage --objects HEAD..$branch)
+	echo "$size $branch"
+done |
+sort -n
+----------
+
+* Compare the on-disk size of branches in one group of refs, excluding
+  another. If you co-mingle objects from multiple remotes in a single
+  repository, this can show which remotes are contributing to the
+  repository size (taking the size of `origin` as a baseline).
++
+----------
+git rev-list --disk-usage --objects --remotes=$suspect --not --remotes=origin
+----------
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 5013daa..6b8ca08 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -212,6 +212,18 @@
 	Only the names of the variables are listed, not their value,
 	even if they are set.
 
+--path-format=(absolute|relative)::
+	Controls the behavior of certain other options. If specified as absolute, the
+	paths printed by those options will be absolute and canonical. If specified as
+	relative, the paths will be relative to the current working directory if that
+	is possible.  The default is option specific.
++
+This option may be specified multiple times and affects only the arguments that
+follow it on the command line, either to the end of the command line or the next
+instance of this option.
+
+The following options are modified by `--path-format`:
+
 --git-dir::
 	Show `$GIT_DIR` if defined. Otherwise show the path to
 	the .git directory. The path shown, when relative, is
@@ -221,13 +233,42 @@
 is not detected to lie in a Git repository or work tree
 print a message to stderr and exit with nonzero status.
 
+--git-common-dir::
+	Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
+
+--resolve-git-dir <path>::
+	Check if <path> is a valid repository or a gitfile that
+	points at a valid repository, and print the location of the
+	repository.  If <path> is a gitfile then the resolved path
+	to the real repository is printed.
+
+--git-path <path>::
+	Resolve "$GIT_DIR/<path>" and takes other path relocation
+	variables such as $GIT_OBJECT_DIRECTORY,
+	$GIT_INDEX_FILE... into account. For example, if
+	$GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
+	--git-path objects/abc" returns /foo/bar/abc.
+
+--show-toplevel::
+	Show the (by default, absolute) path of the top-level directory
+	of the working tree. If there is no working tree, report an error.
+
+--show-superproject-working-tree::
+	Show the absolute path of the root of the superproject's
+	working tree (if exists) that uses the current repository as
+	its submodule.  Outputs nothing if the current repository is
+	not used as a submodule by any project.
+
+--shared-index-path::
+	Show the path to the shared index file in split index mode, or
+	empty if not in split-index mode.
+
+The following options are unaffected by `--path-format`:
+
 --absolute-git-dir::
 	Like `--git-dir`, but its output is always the canonicalized
 	absolute path.
 
---git-common-dir::
-	Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
-
 --is-inside-git-dir::
 	When the current working directory is below the repository
 	directory print "true", otherwise "false".
@@ -242,19 +283,6 @@
 --is-shallow-repository::
 	When the repository is shallow print "true", otherwise "false".
 
---resolve-git-dir <path>::
-	Check if <path> is a valid repository or a gitfile that
-	points at a valid repository, and print the location of the
-	repository.  If <path> is a gitfile then the resolved path
-	to the real repository is printed.
-
---git-path <path>::
-	Resolve "$GIT_DIR/<path>" and takes other path relocation
-	variables such as $GIT_OBJECT_DIRECTORY,
-	$GIT_INDEX_FILE... into account. For example, if
-	$GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse
-	--git-path objects/abc" returns /foo/bar/abc.
-
 --show-cdup::
 	When the command is invoked from a subdirectory, show the
 	path of the top-level directory relative to the current
@@ -265,20 +293,6 @@
 	path of the current directory relative to the top-level
 	directory.
 
---show-toplevel::
-	Show the absolute path of the top-level directory of the working
-	tree. If there is no working tree, report an error.
-
---show-superproject-working-tree::
-	Show the absolute path of the root of the superproject's
-	working tree (if exists) that uses the current repository as
-	its submodule.  Outputs nothing if the current repository is
-	not used as a submodule by any project.
-
---shared-index-path::
-	Show the path to the shared index file in split index mode, or
-	empty if not in split-index mode.
-
 --show-object-format[=(storage|input|output)]::
 	Show the object format (hash algorithm) used for the repository
 	for storage inside the `.git` directory, input, or output. For
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index fd93cd4..c9c7f30 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -111,11 +111,11 @@
 MAPPING AUTHORS
 ---------------
 
-The `.mailmap` feature is used to coalesce together commits by the same
-person in the shortlog, where their name and/or email address was
-spelled differently.
+See linkgit:gitmailmap[5].
 
-include::mailmap.txt[]
+Note that if `git shortlog` is run outside of a repository (to process
+log contents on standard input), it will look for a `.mailmap` file in
+the current directory.
 
 GIT
 ---
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index fcf528c..2b1bc72 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -45,10 +45,13 @@
 include::pretty-formats.txt[]
 
 
-COMMON DIFF OPTIONS
--------------------
+DIFF FORMATTING
+---------------
+The options below can be used to change the way `git show` generates
+diff output.
 
 :git-log: 1
+:diff-merges-default: `dense-combined`
 include::diff-options.txt[]
 
 include::diff-generate-patch.txt[]
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 31f1beb..f1197d6 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -8,8 +8,8 @@
 SYNOPSIS
 --------
 [verse]
-'git stash' list [<options>]
-'git stash' show [<options>] [<stash>]
+'git stash' list [<log-options>]
+'git stash' show [<diff-options>] [<stash>]
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
@@ -67,7 +67,7 @@
 	Instead, all non-option arguments are concatenated to form the stash
 	message.
 
-list [<options>]::
+list [<log-options>]::
 
 	List the stash entries that you currently have.  Each 'stash entry' is
 	listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is
@@ -83,7 +83,7 @@
 The command takes options applicable to the 'git log'
 command to control what is shown and how. See linkgit:git-log[1].
 
-show [<options>] [<stash>]::
+show [<diff-options>] [<stash>]::
 
 	Show the changes recorded in the stash entry as a diff between the
 	stashed contents and the commit back when the stash entry was first
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index c0764e8..83f38e3 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -130,7 +130,7 @@
 --column[=<options>]::
 --no-column::
 	Display untracked files in columns. See configuration variable
-	column.status for option syntax.`--column` and `--no-column`
+	`column.status` for option syntax. `--column` and `--no-column`
 	without options are equivalent to 'always' and 'never'
 	respectively.
 
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 56656d1..31a97a1 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -134,7 +134,7 @@
 --column[=<options>]::
 --no-column::
 	Display tag listing in columns. See configuration variable
-	column.tag for option syntax.`--column` and `--no-column`
+	`column.tag` for option syntax. `--column` and `--no-column`
 	without options are equivalent to 'always' and 'never' respectively.
 +
 This option is only applicable when listing tags without annotation lines.
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index af06128..f1bb1fa 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -97,8 +97,9 @@
 List details of each working tree.  The main working tree is listed first,
 followed by each of the linked working trees.  The output details include
 whether the working tree is bare, the revision currently checked out, the
-branch currently checked out (or "detached HEAD" if none), and "locked" if
-the worktree is locked.
+branch currently checked out (or "detached HEAD" if none), "locked" if
+the worktree is locked, "prunable" if the worktree can be pruned by `prune`
+command.
 
 lock::
 
@@ -143,6 +144,11 @@
 reestablish the connection. If multiple linked working trees are moved,
 running `repair` from any working tree with each tree's new `<path>` as
 an argument, will reestablish the connection to all the specified paths.
++
+If both the main working tree and linked working trees have been moved
+manually, then running `repair` in the main working tree and specifying the
+new `<path>` of each linked working tree will reestablish all connections
+in both directions.
 
 unlock::
 
@@ -226,9 +232,14 @@
 -v::
 --verbose::
 	With `prune`, report all removals.
++
+With `list`, output additional information about worktrees (see below).
 
 --expire <time>::
 	With `prune`, only expire unused working trees older than `<time>`.
++
+With `list`, annotate missing working trees as prunable if they are
+older than `<time>`.
 
 --reason <string>::
 	With `lock`, an explanation why the working tree is locked.
@@ -367,13 +378,46 @@
 /path/to/other-linked-worktree  1234abc  (detached HEAD)
 ------------
 
+The command also shows annotations for each working tree, according to its state.
+These annotations are:
+
+ * `locked`, if the working tree is locked.
+ * `prunable`, if the working tree can be pruned via `git worktree prune`.
+
+------------
+$ git worktree list
+/path/to/linked-worktree    abcd1234 [master]
+/path/to/locked-worktreee   acbd5678 (brancha) locked
+/path/to/prunable-worktree  5678abc  (detached HEAD) prunable
+------------
+
+For these annotations, a reason might also be available and this can be
+seen using the verbose mode. The annotation is then moved to the next line
+indented followed by the additional information.
+
+------------
+$ git worktree list --verbose
+/path/to/linked-worktree              abcd1234 [master]
+/path/to/locked-worktree-no-reason    abcd5678 (detached HEAD) locked
+/path/to/locked-worktree-with-reason  1234abcd (brancha)
+	locked: working tree path is mounted on a portable device
+/path/to/prunable-worktree            5678abc1 (detached HEAD)
+	prunable: gitdir file points to non-existent location
+------------
+
+Note that the annotation is moved to the next line if the additional
+information is available, otherwise it stays on the same line as the
+working tree itself.
+
 Porcelain Format
 ~~~~~~~~~~~~~~~~
 The porcelain format has a line per attribute.  Attributes are listed with a
 label and value separated by a single space.  Boolean attributes (like `bare`
 and `detached`) are listed as a label only, and are present only
-if the value is true.  The first attribute of a working tree is always
-`worktree`, an empty line indicates the end of the record.  For example:
+if the value is true.  Some attributes (like `locked`) can be listed as a label
+only or with a value depending upon whether a reason is available.  The first
+attribute of a working tree is always `worktree`, an empty line indicates the
+end of the record.  For example:
 
 ------------
 $ git worktree list --porcelain
@@ -388,6 +432,33 @@
 HEAD 1234abc1234abc1234abc1234abc1234abc1234a
 detached
 
+worktree /path/to/linked-worktree-locked-no-reason
+HEAD 5678abc5678abc5678abc5678abc5678abc5678c
+branch refs/heads/locked-no-reason
+locked
+
+worktree /path/to/linked-worktree-locked-with-reason
+HEAD 3456def3456def3456def3456def3456def3456b
+branch refs/heads/locked-with-reason
+locked reason why is locked
+
+worktree /path/to/linked-worktree-prunable
+HEAD 1233def1234def1234def1234def1234def1234b
+detached
+prunable gitdir file points to non-existent location
+
+------------
+
+If the lock reason contains "unusual" characters such as newline, they
+are escaped and the entire reason is quoted as explained for the
+configuration variable `core.quotePath` (see linkgit:git-config[1]).
+For Example:
+
+------------
+$ git worktree list --porcelain
+...
+locked "reason\nwhy is locked"
+...
 ------------
 
 EXAMPLES
diff --git a/Documentation/git.txt b/Documentation/git.txt
index a6d4ad0..3a9c449 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -13,7 +13,7 @@
     [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
     [-p|--paginate|-P|--no-pager] [--no-replace-objects] [--bare]
     [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
-    [--super-prefix=<path>]
+    [--super-prefix=<path>] [--config-env <name>=<envvar>]
     <command> [<args>]
 
 DESCRIPTION
@@ -80,6 +80,28 @@
 foo.bar= ...`) sets `foo.bar` to the empty string which `git config
 --type=bool` will convert to `false`.
 
+--config-env=<name>=<envvar>::
+	Like `-c <name>=<value>`, give configuration variable
+	'<name>' a value, where <envvar> is the name of an
+	environment variable from which to retrieve the value. Unlike
+	`-c` there is no shortcut for directly setting the value to an
+	empty string, instead the environment variable itself must be
+	set to the empty string.  It is an error if the `<envvar>` does not exist
+	in the environment. `<envvar>` may not contain an equals sign
+	to avoid ambiguity with `<name>` containing one.
++
+This is useful for cases where you want to pass transitory
+configuration options to git, but are doing so on OS's where
+other processes might be able to read your cmdline
+(e.g. `/proc/self/cmdline`), but not your environ
+(e.g. `/proc/self/environ`). That behavior is the default on
+Linux, but may not be on your system.
++
+Note that this might add security for variables such as
+`http.extraHeader` where the sensitive information is part of
+the value, but not e.g. `url.<base>.insteadOf` where the
+sensitive information can be part of the key.
+
 --exec-path[=<path>]::
 	Path to wherever your core Git programs are installed.
 	This can also be controlled by setting the GIT_EXEC_PATH
diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt
index c970d9f..1c72696 100644
--- a/Documentation/gitdiffcore.txt
+++ b/Documentation/gitdiffcore.txt
@@ -74,6 +74,7 @@
 - diffcore-merge-broken
 - diffcore-pickaxe
 - diffcore-order
+- diffcore-rotate
 
 These are applied in sequence.  The set of filepairs 'git diff-{asterisk}'
 commands find are used as the input to diffcore-break, and
@@ -168,6 +169,26 @@
 number after the "-M" or "-C" option (e.g. "-M8" to tell it to use
 8/10 = 80%).
 
+Note that when rename detection is on but both copy and break
+detection are off, rename detection adds a preliminary step that first
+checks if files are moved across directories while keeping their
+filename the same.  If there is a file added to a directory whose
+contents is sufficiently similar to a file with the same name that got
+deleted from a different directory, it will mark them as renames and
+exclude them from the later quadratic step (the one that pairwise
+compares all unmatched files to find the "best" matches, determined by
+the highest content similarity).  So, for example, if a deleted
+docs/ext.txt and an added docs/config/ext.txt are similar enough, they
+will be marked as a rename and prevent an added docs/ext.md that may
+be even more similar to the deleted docs/ext.txt from being considered
+as the rename destination in the later step.  For this reason, the
+preliminary "match same filename" step uses a bit higher threshold to
+mark a file pair as a rename and stop considering other candidates for
+better matches.  At most, one comparison is done per file in this
+preliminary pass; so if there are several remaining ext.txt files
+throughout the directory hierarchy after exact rename detection, this
+preliminary step will be skipped for those files.
+
 Note.  When the "-C" option is used with `--find-copies-harder`
 option, 'git diff-{asterisk}' commands feed unmodified filepairs to
 diffcore mechanism as well as modified ones.  This lets the copy
@@ -276,6 +297,26 @@
 t
 ------------------------------------------------
 
+diffcore-rotate: For Changing At Which Path Output Starts
+---------------------------------------------------------
+
+This transformation takes one pathname, and rotates the set of
+filepairs so that the filepair for the given pathname comes first,
+optionally discarding the paths that come before it.  This is used
+to implement the `--skip-to` and the `--rotate-to` options.  It is
+an error when the specified pathname is not in the set of filepairs,
+but it is not useful to error out when used with "git log" family of
+commands, because it is unreasonable to expect that a given path
+would be modified by each and every commit shown by the "git log"
+command.  For this reason, when used with "git log", the filepair
+that sorts the same as, or the first one that sorts after, the given
+pathname is where the output starts.
+
+Use of this transformation combined with diffcore-order will produce
+unexpected results, as the input to this transformation is likely
+not sorted when diffcore-order is in effect.
+
+
 SEE ALSO
 --------
 linkgit:git-diff[1],
diff --git a/Documentation/gitmailmap.txt b/Documentation/gitmailmap.txt
new file mode 100644
index 0000000..3fb39f8
--- /dev/null
+++ b/Documentation/gitmailmap.txt
@@ -0,0 +1,123 @@
+gitmailmap(5)
+=============
+
+NAME
+----
+gitmailmap - Map author/committer names and/or E-Mail addresses
+
+SYNOPSIS
+--------
+$GIT_WORK_TREE/.mailmap
+
+
+DESCRIPTION
+-----------
+
+If the file `.mailmap` exists at the toplevel of the repository, or at
+the location pointed to by the `mailmap.file` or `mailmap.blob`
+configuration options (see linkgit:git-config[1]), it
+is used to map author and committer names and email addresses to
+canonical real names and email addresses.
+
+
+SYNTAX
+------
+
+The '#' character begins a comment to the end of line, blank lines
+are ignored.
+
+In the simple form, each line in the file consists of the canonical
+real name of an author, whitespace, and an email address used in the
+commit (enclosed by '<' and '>') to map to the name. For example:
+--
+	Proper Name <commit@email.xx>
+--
+
+The more complex forms are:
+--
+	<proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace only the email part of a commit, and:
+--
+	Proper Name <proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching the specified commit email address, and:
+--
+	Proper Name <proper@email.xx> Commit Name <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching both the specified commit name and email address.
+
+Both E-Mails and names are matched case-insensitively. For example
+this would also match the 'Commit Name <commit&#64;email.xx>' above:
+--
+	Proper Name <proper@email.xx> CoMmIt NaMe <CoMmIt@EmAiL.xX>
+--
+
+EXAMPLES
+--------
+
+Your history contains commits by two authors, Jane
+and Joe, whose names appear in the repository under several forms:
+
+------------
+Joe Developer <joe@example.com>
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com>
+Jane Doe <jane@laptop.(none)>
+Jane D. <jane@desktop.(none)>
+------------
+
+Now suppose that Joe wants his middle name initial used, and Jane
+prefers her family name fully spelled out. A `.mailmap` file to
+correct the names would look like:
+
+------------
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com>
+Jane Doe <jane@desktop.(none)>
+------------
+
+Note that there's no need to map the name for '<jane&#64;laptop.(none)>' to
+only correct the names. However, leaving the obviously broken
+'<jane&#64;laptop.(none)>' and '<jane&#64;desktop.(none)>' E-Mails as-is is
+usually not what you want. A `.mailmap` file which also corrects those
+is:
+
+------------
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com> <jane@laptop.(none)>
+Jane Doe <jane@example.com> <jane@desktop.(none)>
+------------
+
+Finally, let's say that Joe and Jane shared an E-Mail address, but not
+a name, e.g. by having these two commits in the history generated by a
+bug reporting system. I.e. names appearing in history as:
+
+------------
+Joe <bugs@example.com>
+Jane <bugs@example.com>
+------------
+
+A full `.mailmap` file which also handles those cases (an addition of
+two lines to the above example) would be:
+
+------------
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com> <jane@laptop.(none)>
+Jane Doe <jane@example.com> <jane@desktop.(none)>
+Joe R. Developer <joe@example.com> Joe <bugs@example.com>
+Jane Doe <jane@example.com> Jane <bugs@example.com>
+------------
+
+
+
+SEE ALSO
+--------
+linkgit:git-check-mailmap[1]
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/i18n.txt b/Documentation/i18n.txt
index 7e36e5b..6c6baee 100644
--- a/Documentation/i18n.txt
+++ b/Documentation/i18n.txt
@@ -38,7 +38,7 @@
   a warning if the commit log message given to it does not look
   like a valid UTF-8 string, unless you explicitly say your
   project uses a legacy encoding.  The way to say this is to
-  have i18n.commitencoding in `.git/config` file, like this:
+  have `i18n.commitEncoding` in `.git/config` file, like this:
 +
 ------------
 [i18n]
diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt
deleted file mode 100644
index 4a8c276..0000000
--- a/Documentation/mailmap.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-If the file `.mailmap` exists at the toplevel of the repository, or at
-the location pointed to by the mailmap.file or mailmap.blob
-configuration options, it
-is used to map author and committer names and email addresses to
-canonical real names and email addresses.
-
-In the simple form, each line in the file consists of the canonical
-real name of an author, whitespace, and an email address used in the
-commit (enclosed by '<' and '>') to map to the name. For example:
---
-	Proper Name <commit@email.xx>
---
-
-The more complex forms are:
---
-	<proper@email.xx> <commit@email.xx>
---
-which allows mailmap to replace only the email part of a commit, and:
---
-	Proper Name <proper@email.xx> <commit@email.xx>
---
-which allows mailmap to replace both the name and the email of a
-commit matching the specified commit email address, and:
---
-	Proper Name <proper@email.xx> Commit Name <commit@email.xx>
---
-which allows mailmap to replace both the name and the email of a
-commit matching both the specified commit name and email address.
-
-Example 1: Your history contains commits by two authors, Jane
-and Joe, whose names appear in the repository under several forms:
-
-------------
-Joe Developer <joe@example.com>
-Joe R. Developer <joe@example.com>
-Jane Doe <jane@example.com>
-Jane Doe <jane@laptop.(none)>
-Jane D. <jane@desktop.(none)>
-------------
-
-Now suppose that Joe wants his middle name initial used, and Jane
-prefers her family name fully spelled out. A proper `.mailmap` file
-would look like:
-
-------------
-Jane Doe         <jane@desktop.(none)>
-Joe R. Developer <joe@example.com>
-------------
-
-Note how there is no need for an entry for `<jane@laptop.(none)>`, because the
-real name of that author is already correct.
-
-Example 2: Your repository contains commits from the following
-authors:
-
-------------
-nick1 <bugs@company.xx>
-nick2 <bugs@company.xx>
-nick2 <nick2@company.xx>
-santa <me@company.xx>
-claus <me@company.xx>
-CTO <cto@coompany.xx>
-------------
-
-Then you might want a `.mailmap` file that looks like:
-------------
-<cto@company.xx>                       <cto@coompany.xx>
-Some Dude <some@dude.xx>         nick1 <bugs@company.xx>
-Other Author <other@author.xx>   nick2 <bugs@company.xx>
-Other Author <other@author.xx>         <nick2@company.xx>
-Santa Claus <santa.claus@northpole.xx> <me@company.xx>
-------------
-
-Use hash '#' for comments that are either on their own line, or after
-the email address.
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 84bbc74..6b59e28 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -252,7 +252,15 @@
 			  interpreted by
 			  linkgit:git-interpret-trailers[1]. The
 			  `trailers` string may be followed by a colon
-			  and zero or more comma-separated options:
+			  and zero or more comma-separated options.
+			  If any option is provided multiple times the
+			  last occurance wins.
++
+The boolean options accept an optional value `[=<BOOL>]`. The values
+`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
+sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
+option is given with no value, it's enabled.
++
 ** 'key=<K>': only show trailers with specified key. Matching is done
    case-insensitively and trailing colon is optional. If option is
    given multiple times trailer lines matching any of the keys are
@@ -261,27 +269,25 @@
    desired it can be disabled with `only=false`.  E.g.,
    `%(trailers:key=Reviewed-by)` shows trailer lines with key
    `Reviewed-by`.
-** 'only[=val]': select whether non-trailer lines from the trailer
-   block should be included. The `only` keyword may optionally be
-   followed by an equal sign and one of `true`, `on`, `yes` to omit or
-   `false`, `off`, `no` to show the non-trailer lines. If option is
-   given without value it is enabled. If given multiple times the last
-   value is used.
+** 'only[=<BOOL>]': select whether non-trailer lines from the trailer
+   block should be included.
 ** 'separator=<SEP>': specify a separator inserted between trailer
    lines. When this option is not given each trailer line is
    terminated with a line feed character. The string SEP may contain
    the literal formatting codes described above. To use comma as
    separator one must use `%x2C` as it would otherwise be parsed as
-   next option. If separator option is given multiple times only the
-   last one is used. E.g., `%(trailers:key=Ticket,separator=%x2C )`
+   next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
    shows all trailer lines whose key is "Ticket" separated by a comma
    and a space.
-** 'unfold[=val]': make it behave as if interpret-trailer's `--unfold`
-   option was given. In same way as to for `only` it can be followed
-   by an equal sign and explicit value. E.g.,
+** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold`
+   option was given. E.g.,
    `%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
-** 'valueonly[=val]': skip over the key part of the trailer line and only
-   show the value part. Also this optionally allows explicit value.
+** 'keyonly[=<BOOL>]': only show the key part of the trailer.
+** 'valueonly[=<BOOL>]': only show the value part of the trailer.
+** 'key_value_separator=<SEP>': specify a separator inserted between
+   trailer lines. When this option is not given each trailer key-value
+   pair is separated by ": ". Otherwise it shares the same semantics
+   as 'separator=<SEP>' above.
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 0023790..b1c8f86 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -129,6 +129,11 @@
 	adjusting to updated upstream from time to time, and
 	this option allows you to ignore the individual commits
 	brought in to your history by such a merge.
+ifdef::git-log[]
++
+This option also changes default diff format for merge commits
+to `first-parent`, see `--diff-merges=first-parent` for details.
+endif::git-log[]
 
 --not::
 	Reverses the meaning of the '{caret}' prefix (or lack thereof)
@@ -222,6 +227,15 @@
 	test the exit status to see if a range of objects is fully
 	connected (or not).  It is faster than redirecting stdout
 	to `/dev/null` as the output does not have to be formatted.
+
+--disk-usage::
+	Suppress normal output; instead, print the sum of the bytes used
+	for on-disk storage by the selected commits or objects. This is
+	equivalent to piping the output into `git cat-file
+	--batch-check='%(objectsize:disk)'`, except that it runs much
+	faster (especially with `--use-bitmap-index`). See the `CAVEATS`
+	section in linkgit:git-cat-file[1] for the limitations of what
+	"on-disk storage" means.
 endif::git-rev-list[]
 
 --cherry-mark::
diff --git a/Documentation/technical/chunk-format.txt b/Documentation/technical/chunk-format.txt
new file mode 100644
index 0000000..593614f
--- /dev/null
+++ b/Documentation/technical/chunk-format.txt
@@ -0,0 +1,116 @@
+Chunk-based file formats
+========================
+
+Some file formats in Git use a common concept of "chunks" to describe
+sections of the file. This allows structured access to a large file by
+scanning a small "table of contents" for the remaining data. This common
+format is used by the `commit-graph` and `multi-pack-index` files. See
+link:technical/pack-format.html[the `multi-pack-index` format] and
+link:technical/commit-graph-format.html[the `commit-graph` format] for
+how they use the chunks to describe structured data.
+
+A chunk-based file format begins with some header information custom to
+that format. That header should include enough information to identify
+the file type, format version, and number of chunks in the file. From this
+information, that file can determine the start of the chunk-based region.
+
+The chunk-based region starts with a table of contents describing where
+each chunk starts and ends. This consists of (C+1) rows of 12 bytes each,
+where C is the number of chunks. Consider the following table:
+
+  | Chunk ID (4 bytes) | Chunk Offset (8 bytes) |
+  |--------------------|------------------------|
+  | ID[0]              | OFFSET[0]              |
+  | ...                | ...                    |
+  | ID[C]              | OFFSET[C]              |
+  | 0x0000             | OFFSET[C+1]            |
+
+Each row consists of a 4-byte chunk identifier (ID) and an 8-byte offset.
+Each integer is stored in network-byte order.
+
+The chunk identifier `ID[i]` is a label for the data stored within this
+fill from `OFFSET[i]` (inclusive) to `OFFSET[i+1]` (exclusive). Thus, the
+size of the `i`th chunk is equal to the difference between `OFFSET[i+1]`
+and `OFFSET[i]`. This requires that the chunk data appears contiguously
+in the same order as the table of contents.
+
+The final entry in the table of contents must be four zero bytes. This
+confirms that the table of contents is ending and provides the offset for
+the end of the chunk-based data.
+
+Note: The chunk-based format expects that the file contains _at least_ a
+trailing hash after `OFFSET[C+1]`.
+
+Functions for working with chunk-based file formats are declared in
+`chunk-format.h`. Using these methods provide extra checks that assist
+developers when creating new file formats.
+
+Writing chunk-based file formats
+--------------------------------
+
+To write a chunk-based file format, create a `struct chunkfile` by
+calling `init_chunkfile()` and pass a `struct hashfile` pointer. The
+caller is responsible for opening the `hashfile` and writing header
+information so the file format is identifiable before the chunk-based
+format begins.
+
+Then, call `add_chunk()` for each chunk that is intended for write. This
+populates the `chunkfile` with information about the order and size of
+each chunk to write. Provide a `chunk_write_fn` function pointer to
+perform the write of the chunk data upon request.
+
+Call `write_chunkfile()` to write the table of contents to the `hashfile`
+followed by each of the chunks. This will verify that each chunk wrote
+the expected amount of data so the table of contents is correct.
+
+Finally, call `free_chunkfile()` to clear the `struct chunkfile` data. The
+caller is responsible for finalizing the `hashfile` by writing the trailing
+hash and closing the file.
+
+Reading chunk-based file formats
+--------------------------------
+
+To read a chunk-based file format, the file must be opened as a
+memory-mapped region. The chunk-format API expects that the entire file
+is mapped as a contiguous memory region.
+
+Initialize a `struct chunkfile` pointer with `init_chunkfile(NULL)`.
+
+After reading the header information from the beginning of the file,
+including the chunk count, call `read_table_of_contents()` to populate
+the `struct chunkfile` with the list of chunks, their offsets, and their
+sizes.
+
+Extract the data information for each chunk using `pair_chunk()` or
+`read_chunk()`:
+
+* `pair_chunk()` assigns a given pointer with the location inside the
+  memory-mapped file corresponding to that chunk's offset. If the chunk
+  does not exist, then the pointer is not modified.
+
+* `read_chunk()` takes a `chunk_read_fn` function pointer and calls it
+  with the appropriate initial pointer and size information. The function
+  is not called if the chunk does not exist. Use this method to read chunks
+  if you need to perform immediate parsing or if you need to execute logic
+  based on the size of the chunk.
+
+After calling these methods, call `free_chunkfile()` to clear the
+`struct chunkfile` data. This will not close the memory-mapped region.
+Callers are expected to own that data for the timeframe the pointers into
+the region are needed.
+
+Examples
+--------
+
+These file formats use the chunk-format API, and can be used as examples
+for future formats:
+
+* *commit-graph:* see `write_commit_graph_file()` and `parse_commit_graph()`
+  in `commit-graph.c` for how the chunk-format API is used to write and
+  parse the commit-graph file format documented in
+  link:technical/commit-graph-format.html[the commit-graph file format].
+
+* *multi-pack-index:* see `write_midx_internal()` and `load_multi_pack_index()`
+  in `midx.c` for how the chunk-format API is used to write and
+  parse the multi-pack-index file format documented in
+  link:technical/pack-format.html[the multi-pack-index file format].
diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt
index b3b5888..87971c2 100644
--- a/Documentation/technical/commit-graph-format.txt
+++ b/Documentation/technical/commit-graph-format.txt
@@ -4,11 +4,7 @@
 The Git commit graph stores a list of commit OIDs and some associated
 metadata, including:
 
-- The generation number of the commit. Commits with no parents have
-  generation number 1; commits with parents have generation number
-  one more than the maximum generation number of its parents. We
-  reserve zero as special, and can be used to mark a generation
-  number invalid or as "not computed".
+- The generation number of the commit.
 
 - The root tree OID.
 
@@ -65,6 +61,9 @@
       the length using the next chunk position if necessary.) Each chunk
       ID appears at most once.
 
+  The CHUNK LOOKUP matches the table of contents from
+  link:technical/chunk-format.html[the chunk-based file format].
+
   The remaining data in the body is described one chunk at a time, and
   these chunks may be given in any order. Chunks are required unless
   otherwise specified.
@@ -86,13 +85,33 @@
       position. If there are more than two parents, the second value
       has its most-significant bit on and the other bits store an array
       position into the Extra Edge List chunk.
-    * The next 8 bytes store the generation number of the commit and
+    * The next 8 bytes store the topological level (generation number v1)
+      of the commit and
       the commit time in seconds since EPOCH. The generation number
       uses the higher 30 bits of the first 4 bytes, while the commit
       time uses the 32 bits of the second 4 bytes, along with the lowest
       2 bits of the lowest byte, storing the 33rd and 34th bit of the
       commit time.
 
+  Generation Data (ID: {'G', 'D', 'A', 'T' }) (N * 4 bytes) [Optional]
+    * This list of 4-byte values store corrected commit date offsets for the
+      commits, arranged in the same order as commit data chunk.
+    * If the corrected commit date offset cannot be stored within 31 bits,
+      the value has its most-significant bit on and the other bits store
+      the position of corrected commit date into the Generation Data Overflow
+      chunk.
+    * Generation Data chunk is present only when commit-graph file is written
+      by compatible versions of Git and in case of split commit-graph chains,
+      the topmost layer also has Generation Data chunk.
+
+  Generation Data Overflow (ID: {'G', 'D', 'O', 'V' }) [Optional]
+    * This list of 8-byte values stores the corrected commit date offsets
+      for commits with corrected commit date offsets that cannot be
+      stored within 31 bits.
+    * Generation Data Overflow chunk is present only when Generation Data
+      chunk is present and atleast one corrected commit date offset cannot
+      be stored within 31 bits.
+
   Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
       This list of 4-byte values store the second through nth parents for
       all octopus merges. The second parent value in the commit data stores
diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt
index f14a765..f05e7bd 100644
--- a/Documentation/technical/commit-graph.txt
+++ b/Documentation/technical/commit-graph.txt
@@ -38,14 +38,31 @@
 
 Values 1-4 satisfy the requirements of parse_commit_gently().
 
-Define the "generation number" of a commit recursively as follows:
+There are two definitions of generation number:
+1. Corrected committer dates (generation number v2)
+2. Topological levels (generation nummber v1)
 
- * A commit with no parents (a root commit) has generation number one.
+Define "corrected committer date" of a commit recursively as follows:
 
- * A commit with at least one parent has generation number one more than
-   the largest generation number among its parents.
+ * A commit with no parents (a root commit) has corrected committer date
+    equal to its committer date.
 
-Equivalently, the generation number of a commit A is one more than the
+ * A commit with at least one parent has corrected committer date equal to
+    the maximum of its commiter date and one more than the largest corrected
+    committer date among its parents.
+
+ * As a special case, a root commit with timestamp zero has corrected commit
+    date of 1, to be able to distinguish it from GENERATION_NUMBER_ZERO
+    (that is, an uncomputed corrected commit date).
+
+Define the "topological level" of a commit recursively as follows:
+
+ * A commit with no parents (a root commit) has topological level of one.
+
+ * A commit with at least one parent has topological level one more than
+   the largest topological level among its parents.
+
+Equivalently, the topological level of a commit A is one more than the
 length of a longest path from A to a root commit. The recursive definition
 is easier to use for computation and observing the following property:
 
@@ -60,6 +77,9 @@
     generation numbers, then we always expand the boundary commit with highest
     generation number and can easily detect the stopping condition.
 
+The property applies to both versions of generation number, that is both
+corrected committer dates and topological levels.
+
 This property can be used to significantly reduce the time it takes to
 walk commits and determine topological relationships. Without generation
 numbers, the general heuristic is the following:
@@ -67,7 +87,9 @@
     If A and B are commits with commit time X and Y, respectively, and
     X < Y, then A _probably_ cannot reach B.
 
-This heuristic is currently used whenever the computation is allowed to
+In absence of corrected commit dates (for example, old versions of Git or
+mixed generation graph chains),
+this heuristic is currently used whenever the computation is allowed to
 violate topological relationships due to clock skew (such as "git log"
 with default order), but is not used when the topological order is
 required (such as merge base calculations, "git log --graph").
@@ -77,7 +99,7 @@
 generation number and walk until reaching commits with known generation
 number.
 
-We use the macro GENERATION_NUMBER_INFINITY = 0xFFFFFFFF to mark commits not
+We use the macro GENERATION_NUMBER_INFINITY to mark commits not
 in the commit-graph file. If a commit-graph file was written by a version
 of Git that did not compute generation numbers, then those commits will
 have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
@@ -93,12 +115,12 @@
 walking a few extra commits, but the simplicity in dealing with commits
 with generation number *_INFINITY or *_ZERO is valuable.
 
-We use the macro GENERATION_NUMBER_MAX = 0x3FFFFFFF to for commits whose
-generation numbers are computed to be at least this value. We limit at
-this value since it is the largest value that can be stored in the
-commit-graph file using the 30 bits available to generation numbers. This
-presents another case where a commit can have generation number equal to
-that of a parent.
+We use the macro GENERATION_NUMBER_V1_MAX = 0x3FFFFFFF for commits whose
+topological levels (generation number v1) are computed to be at least
+this value. We limit at this value since it is the largest value that
+can be stored in the commit-graph file using the 30 bits available
+to topological levels. This presents another case where a commit can
+have generation number equal to that of a parent.
 
 Design Details
 --------------
@@ -267,6 +289,35 @@
 number of commits) could be extracted into config settings for full
 flexibility.
 
+## Handling Mixed Generation Number Chains
+
+With the introduction of generation number v2 and generation data chunk, the
+following scenario is possible:
+
+1. "New" Git writes a commit-graph with the corrected commit dates.
+2. "Old" Git writes a split commit-graph on top without corrected commit dates.
+
+A naive approach of using the newest available generation number from
+each layer would lead to violated expectations: the lower layer would
+use corrected commit dates which are much larger than the topological
+levels of the higher layer. For this reason, Git inspects the topmost
+layer to see if the layer is missing corrected commit dates. In such a case
+Git only uses topological level for generation numbers.
+
+When writing a new layer in split commit-graph, we write corrected commit
+dates if the topmost layer has corrected commit dates written. This
+guarantees that if a layer has corrected commit dates, all lower layers
+must have corrected commit dates as well.
+
+When merging layers, we do not consider whether the merged layers had corrected
+commit dates. Instead, the new layer will have corrected commit dates if the
+layer below the new layer has corrected commit dates.
+
+While writing or merging layers, if the new layer is the only layer, it will
+have corrected commit dates when written by compatible versions of Git. Thus,
+rewriting split commit-graph as a single file (`--split=replace`) creates a
+single layer with corrected commit dates.
+
 ## Deleting graph-{hash} files
 
 After a new tip file is written, some `graph-{hash}` files may no longer
diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt
index 6fd20eb..7c1630b 100644
--- a/Documentation/technical/hash-function-transition.txt
+++ b/Documentation/technical/hash-function-transition.txt
@@ -33,16 +33,9 @@
 
 Git v2.13.0 and later subsequently moved to a hardened SHA-1
 implementation by default, which isn't vulnerable to the SHAttered
-attack.
+attack, but SHA-1 is still weak.
 
-Thus Git has in effect already migrated to a new hash that isn't SHA-1
-and doesn't share its vulnerabilities, its new hash function just
-happens to produce exactly the same output for all known inputs,
-except two PDFs published by the SHAttered researchers, and the new
-implementation (written by those researchers) claims to detect future
-cryptanalytic collision attacks.
-
-Regardless, it's considered prudent to move past any variant of SHA-1
+Thus it's considered prudent to move past any variant of SHA-1
 to a new hash. There's no guarantee that future attacks on SHA-1 won't
 be published in the future, and those attacks may not have viable
 mitigations.
@@ -57,6 +50,38 @@
 and safe error checking, but other hash functions are equally suitable
 that are believed to be cryptographically secure.
 
+Choice of Hash
+--------------
+The hash to replace the hardened SHA-1 should be stronger than SHA-1
+was: we would like it to be trustworthy and useful in practice for at
+least 10 years.
+
+Some other relevant properties:
+
+1. A 256-bit hash (long enough to match common security practice; not
+   excessively long to hurt performance and disk usage).
+
+2. High quality implementations should be widely available (e.g., in
+   OpenSSL and Apple CommonCrypto).
+
+3. The hash function's properties should match Git's needs (e.g. Git
+   requires collision and 2nd preimage resistance and does not require
+   length extension resistance).
+
+4. As a tiebreaker, the hash should be fast to compute (fortunately
+   many contenders are faster than SHA-1).
+
+There were several contenders for a successor hash to SHA-1, including
+SHA-256, SHA-512/256, SHA-256x16, K12, and BLAKE2bp-256.
+
+In late 2018 the project picked SHA-256 as its successor hash.
+
+See 0ed8d8da374 (doc hash-function-transition: pick SHA-256 as
+NewHash, 2018-08-04) and numerous mailing list threads at the time,
+particularly the one starting at
+https://lore.kernel.org/git/20180609224913.GC38834@genre.crustytoothpaste.net/
+for more information.
+
 Goals
 -----
 1. The transition to SHA-256 can be done one local repository at a time.
@@ -94,7 +119,7 @@
 --------
 We introduce a new repository format extension. Repositories with this
 extension enabled use SHA-256 instead of SHA-1 to name their objects.
-This affects both object names and object content --- both the names
+This affects both object names and object content -- both the names
 of objects and all references to other objects within an object are
 switched to the new hash function.
 
@@ -107,7 +132,7 @@
 interchangeably.
 
 "git cat-file" and "git hash-object" gain options to display an object
-in its sha1 form and write an object given its sha1 form. This
+in its SHA-1 form and write an object given its SHA-1 form. This
 requires all objects referenced by that object to be present in the
 object database so that they can be named using the appropriate name
 (using the bidirectional hash mapping).
@@ -115,7 +140,7 @@
 Fetches from a SHA-1 based server convert the fetched objects into
 SHA-256 form and record the mapping in the bidirectional mapping table
 (see below for details). Pushes to a SHA-1 based server convert the
-objects being pushed into sha1 form so the server does not have to be
+objects being pushed into SHA-1 form so the server does not have to be
 aware of the hash function the client is using.
 
 Detailed Design
@@ -151,38 +176,38 @@
 
 Object names
 ~~~~~~~~~~~~
-Objects can be named by their 40 hexadecimal digit sha1-name or 64
-hexadecimal digit sha256-name, plus names derived from those (see
+Objects can be named by their 40 hexadecimal digit SHA-1 name or 64
+hexadecimal digit SHA-256 name, plus names derived from those (see
 gitrevisions(7)).
 
-The sha1-name of an object is the SHA-1 of the concatenation of its
-type, length, a nul byte, and the object's sha1-content. This is the
+The SHA-1 name of an object is the SHA-1 of the concatenation of its
+type, length, a nul byte, and the object's SHA-1 content. This is the
 traditional <sha1> used in Git to name objects.
 
-The sha256-name of an object is the SHA-256 of the concatenation of its
-type, length, a nul byte, and the object's sha256-content.
+The SHA-256 name of an object is the SHA-256 of the concatenation of its
+type, length, a nul byte, and the object's SHA-256 content.
 
 Object format
 ~~~~~~~~~~~~~
 The content as a byte sequence of a tag, commit, or tree object named
-by sha1 and sha256 differ because an object named by sha256-name refers to
-other objects by their sha256-names and an object named by sha1-name
-refers to other objects by their sha1-names.
+by SHA-1 and SHA-256 differ because an object named by SHA-256 name refers to
+other objects by their SHA-256 names and an object named by SHA-1 name
+refers to other objects by their SHA-1 names.
 
-The sha256-content of an object is the same as its sha1-content, except
-that objects referenced by the object are named using their sha256-names
-instead of sha1-names. Because a blob object does not refer to any
-other object, its sha1-content and sha256-content are the same.
+The SHA-256 content of an object is the same as its SHA-1 content, except
+that objects referenced by the object are named using their SHA-256 names
+instead of SHA-1 names. Because a blob object does not refer to any
+other object, its SHA-1 content and SHA-256 content are the same.
 
-The format allows round-trip conversion between sha256-content and
-sha1-content.
+The format allows round-trip conversion between SHA-256 content and
+SHA-1 content.
 
 Object storage
 ~~~~~~~~~~~~~~
 Loose objects use zlib compression and packed objects use the packed
 format described in Documentation/technical/pack-format.txt, just like
-today. The content that is compressed and stored uses sha256-content
-instead of sha1-content.
+today. The content that is compressed and stored uses SHA-256 content
+instead of SHA-1 content.
 
 Pack index
 ~~~~~~~~~~
@@ -191,21 +216,21 @@
 network byte order):
 
 - A header appears at the beginning and consists of the following:
-  - The 4-byte pack index signature: '\377t0c'
-  - 4-byte version number: 3
-  - 4-byte length of the header section, including the signature and
+  * The 4-byte pack index signature: '\377t0c'
+  * 4-byte version number: 3
+  * 4-byte length of the header section, including the signature and
     version number
-  - 4-byte number of objects contained in the pack
-  - 4-byte number of object formats in this pack index: 2
-  - For each object format:
-    - 4-byte format identifier (e.g., 'sha1' for SHA-1)
-    - 4-byte length in bytes of shortened object names. This is the
+  * 4-byte number of objects contained in the pack
+  * 4-byte number of object formats in this pack index: 2
+  * For each object format:
+    ** 4-byte format identifier (e.g., 'sha1' for SHA-1)
+    ** 4-byte length in bytes of shortened object names. This is the
       shortest possible length needed to make names in the shortened
       object name table unambiguous.
-    - 4-byte integer, recording where tables relating to this format
+    ** 4-byte integer, recording where tables relating to this format
       are stored in this index file, as an offset from the beginning.
-  - 4-byte offset to the trailer from the beginning of this file.
-  - Zero or more additional key/value pairs (4-byte key, 4-byte
+  * 4-byte offset to the trailer from the beginning of this file.
+  * Zero or more additional key/value pairs (4-byte key, 4-byte
     value). Only one key is supported: 'PSRC'. See the "Loose objects
     and unreachable objects" section for supported values and how this
     is used.  All other keys are reserved. Readers must ignore
@@ -213,37 +238,36 @@
 - Zero or more NUL bytes. This can optionally be used to improve the
   alignment of the full object name table below.
 - Tables for the first object format:
-  - A sorted table of shortened object names.  These are prefixes of
+  * A sorted table of shortened object names.  These are prefixes of
     the names of all objects in this pack file, packed together
     without offset values to reduce the cache footprint of the binary
     search for a specific object name.
 
-  - A table of full object names in pack order. This allows resolving
+  * A table of full object names in pack order. This allows resolving
     a reference to "the nth object in the pack file" (from a
     reachability bitmap or from the next table of another object
     format) to its object name.
 
-  - A table of 4-byte values mapping object name order to pack order.
+  * A table of 4-byte values mapping object name order to pack order.
     For an object in the table of sorted shortened object names, the
     value at the corresponding index in this table is the index in the
     previous table for that same object.
-
     This can be used to look up the object in reachability bitmaps or
     to look up its name in another object format.
 
-  - A table of 4-byte CRC32 values of the packed object data, in the
+  * A table of 4-byte CRC32 values of the packed object data, in the
     order that the objects appear in the pack file. This is to allow
     compressed data to be copied directly from pack to pack during
     repacking without undetected data corruption.
 
-  - A table of 4-byte offset values. For an object in the table of
+  * A table of 4-byte offset values. For an object in the table of
     sorted shortened object names, the value at the corresponding
     index in this table indicates where that object can be found in
     the pack file. These are usually 31-bit pack file offsets, but
     large offsets are encoded as an index into the next table with the
     most significant bit set.
 
-  - A table of 8-byte offset entries (empty for pack files less than
+  * A table of 8-byte offset entries (empty for pack files less than
     2 GiB). Pack files are organized with heavily used objects toward
     the front, so most object references should not need to refer to
     this table.
@@ -252,10 +276,10 @@
   up to and not including the table of CRC32 values.
 - Zero or more NUL bytes.
 - The trailer consists of the following:
-  - A copy of the 20-byte SHA-256 checksum at the end of the
+  * A copy of the 20-byte SHA-256 checksum at the end of the
     corresponding packfile.
 
-  - 20-byte SHA-256 checksum of all of the above.
+  * 20-byte SHA-256 checksum of all of the above.
 
 Loose object index
 ~~~~~~~~~~~~~~~~~~
@@ -288,18 +312,18 @@
 
 Translation table
 ~~~~~~~~~~~~~~~~~
-The index files support a bidirectional mapping between sha1-names
-and sha256-names. The lookup proceeds similarly to ordinary object
-lookups. For example, to convert a sha1-name to a sha256-name:
+The index files support a bidirectional mapping between SHA-1 names
+and SHA-256 names. The lookup proceeds similarly to ordinary object
+lookups. For example, to convert a SHA-1 name to a SHA-256 name:
 
  1. Look for the object in idx files. If a match is present in the
-    idx's sorted list of truncated sha1-names, then:
-    a. Read the corresponding entry in the sha1-name order to pack
+    idx's sorted list of truncated SHA-1 names, then:
+    a. Read the corresponding entry in the SHA-1 name order to pack
        name order mapping.
-    b. Read the corresponding entry in the full sha1-name table to
+    b. Read the corresponding entry in the full SHA-1 name table to
        verify we found the right object. If it is, then
-    c. Read the corresponding entry in the full sha256-name table.
-       That is the object's sha256-name.
+    c. Read the corresponding entry in the full SHA-256 name table.
+       That is the object's SHA-256 name.
  2. Check for a loose object. Read lines from loose-object-idx until
     we find a match.
 
@@ -313,10 +337,10 @@
 the new objects to the corresponding index, this mapping is possible
 for all objects in the object store.
 
-Reading an object's sha1-content
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The sha1-content of an object can be read by converting all sha256-names
-its sha256-content references to sha1-names using the translation table.
+Reading an object's SHA-1 content
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The SHA-1 content of an object can be read by converting all SHA-256 names
+of its SHA-256 content references to SHA-1 names using the translation table.
 
 Fetch
 ~~~~~
@@ -339,7 +363,7 @@
 1. index-pack: inflate each object in the packfile and compute its
    SHA-1. Objects can contain deltas in OBJ_REF_DELTA format against
    objects the client has locally. These objects can be looked up
-   using the translation table and their sha1-content read as
+   using the translation table and their SHA-1 content read as
    described above to resolve the deltas.
 2. topological sort: starting at the "want"s from the negotiation
    phase, walk through objects in the pack and emit a list of them,
@@ -348,12 +372,12 @@
    (This list only contains objects reachable from the "wants". If the
    pack from the server contained additional extraneous objects, then
    they will be discarded.)
-3. convert to sha256: open a new (sha256) packfile. Read the topologically
+3. convert to SHA-256: open a new SHA-256 packfile. Read the topologically
    sorted list just generated. For each object, inflate its
-   sha1-content, convert to sha256-content, and write it to the sha256
-   pack. Record the new sha1<->sha256 mapping entry for use in the idx.
+   SHA-1 content, convert to SHA-256 content, and write it to the SHA-256
+   pack. Record the new SHA-1<-->SHA-256 mapping entry for use in the idx.
 4. sort: reorder entries in the new pack to match the order of objects
-   in the pack the server generated and include blobs. Write a sha256 idx
+   in the pack the server generated and include blobs. Write a SHA-256 idx
    file
 5. clean up: remove the SHA-1 based pack file, index, and
    topologically sorted list obtained from the server in steps 1
@@ -378,19 +402,20 @@
 Push
 ~~~~
 Push is simpler than fetch because the objects referenced by the
-pushed objects are already in the translation table. The sha1-content
+pushed objects are already in the translation table. The SHA-1 content
 of each object being pushed can be read as described in the "Reading
-an object's sha1-content" section to generate the pack written by git
+an object's SHA-1 content" section to generate the pack written by git
 send-pack.
 
 Signed Commits
 ~~~~~~~~~~~~~~
 We add a new field "gpgsig-sha256" to the commit object format to allow
 signing commits without relying on SHA-1. It is similar to the
-existing "gpgsig" field. Its signed payload is the sha256-content of the
+existing "gpgsig" field. Its signed payload is the SHA-256 content of the
 commit object with any "gpgsig" and "gpgsig-sha256" fields removed.
 
 This means commits can be signed
+
 1. using SHA-1 only, as in existing signed commit objects
 2. using both SHA-1 and SHA-256, by using both gpgsig-sha256 and gpgsig
    fields.
@@ -404,10 +429,11 @@
 ~~~~~~~~~~~
 We add a new field "gpgsig-sha256" to the tag object format to allow
 signing tags without relying on SHA-1. Its signed payload is the
-sha256-content of the tag with its gpgsig-sha256 field and "-----BEGIN PGP
+SHA-256 content of the tag with its gpgsig-sha256 field and "-----BEGIN PGP
 SIGNATURE-----" delimited in-body signature removed.
 
 This means tags can be signed
+
 1. using SHA-1 only, as in existing signed tag objects
 2. using both SHA-1 and SHA-256, by using gpgsig-sha256 and an in-body
    signature.
@@ -415,11 +441,11 @@
 
 Mergetag embedding
 ~~~~~~~~~~~~~~~~~~
-The mergetag field in the sha1-content of a commit contains the
-sha1-content of a tag that was merged by that commit.
+The mergetag field in the SHA-1 content of a commit contains the
+SHA-1 content of a tag that was merged by that commit.
 
-The mergetag field in the sha256-content of the same commit contains the
-sha256-content of the same tag.
+The mergetag field in the SHA-256 content of the same commit contains the
+SHA-256 content of the same tag.
 
 Submodules
 ~~~~~~~~~~
@@ -494,7 +520,7 @@
 -------
 Invalid objects
 ~~~~~~~~~~~~~~~
-The conversion from sha1-content to sha256-content retains any
+The conversion from SHA-1 content to SHA-256 content retains any
 brokenness in the original object (e.g., tree entry modes encoded with
 leading 0, tree objects whose paths are not sorted correctly, and
 commit objects without an author or committer). This is a deliberate
@@ -513,15 +539,15 @@
 
 Alternates
 ~~~~~~~~~~
-For the same reason, a sha256 repository cannot borrow objects from a
-sha1 repository using objects/info/alternates or
+For the same reason, a SHA-256 repository cannot borrow objects from a
+SHA-1 repository using objects/info/alternates or
 $GIT_ALTERNATE_OBJECT_REPOSITORIES.
 
 git notes
 ~~~~~~~~~
-The "git notes" tool annotates objects using their sha1-name as key.
+The "git notes" tool annotates objects using their SHA-1 name as key.
 This design does not describe a way to migrate notes trees to use
-sha256-names. That migration is expected to happen separately (for
+SHA-256 names. That migration is expected to happen separately (for
 example using a file at the root of the notes tree to describe which
 hash it uses).
 
@@ -555,7 +581,7 @@
 
 	Git 2.12
 
-Does this mean Git v2.12.0 is the commit with sha1-name
+Does this mean Git v2.12.0 is the commit with SHA-1 name
 e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7 or the commit with
 new-40-digit-hash-name e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7?
 
@@ -598,44 +624,12 @@
 particular revision specifier and for output, overriding the mode. For
 example:
 
-git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}
-
-Choice of Hash
---------------
-In early 2005, around the time that Git was written, Xiaoyun Wang,
-Yiqun Lisa Yin, and Hongbo Yu announced an attack finding SHA-1
-collisions in 2^69 operations. In August they published details.
-Luckily, no practical demonstrations of a collision in full SHA-1 were
-published until 10 years later, in 2017.
-
-Git v2.13.0 and later subsequently moved to a hardened SHA-1
-implementation by default that mitigates the SHAttered attack, but
-SHA-1 is still believed to be weak.
-
-The hash to replace this hardened SHA-1 should be stronger than SHA-1
-was: we would like it to be trustworthy and useful in practice for at
-least 10 years.
-
-Some other relevant properties:
-
-1. A 256-bit hash (long enough to match common security practice; not
-   excessively long to hurt performance and disk usage).
-
-2. High quality implementations should be widely available (e.g., in
-   OpenSSL and Apple CommonCrypto).
-
-3. The hash function's properties should match Git's needs (e.g. Git
-   requires collision and 2nd preimage resistance and does not require
-   length extension resistance).
-
-4. As a tiebreaker, the hash should be fast to compute (fortunately
-   many contenders are faster than SHA-1).
-
-We choose SHA-256.
+    git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}
 
 Transition plan
 ---------------
 Some initial steps can be implemented independently of one another:
+
 - adding a hash function API (vtable)
 - teaching fsck to tolerate the gpgsig-sha256 field
 - excluding gpgsig-* from the fields copied by "git commit --amend"
@@ -647,9 +641,9 @@
 - introducing index v3
 - adding support for the PSRC field and safer object pruning
 
-
 The first user-visible change is the introduction of the objectFormat
 extension (without compatObjectFormat). This requires:
+
 - teaching fsck about this mode of operation
 - using the hash function API (vtable) when computing object names
 - signing objects and verifying signatures
@@ -657,6 +651,7 @@
   repository
 
 Next comes introduction of compatObjectFormat:
+
 - implementing the loose-object-idx
 - translating object names between object formats
 - translating object content between object formats
@@ -669,10 +664,11 @@
   "Object names on the command line" above)
 
 The next step is supporting fetches and pushes to SHA-1 repositories:
+
 - allow pushes to a repository using the compat format
 - generate a topologically sorted list of the SHA-1 names of fetched
   objects
-- convert the fetched packfile to sha256 format and generate an idx
+- convert the fetched packfile to SHA-256 format and generate an idx
   file
 - re-sort to match the order of objects in the fetched packfile
 
@@ -734,6 +730,7 @@
 Objects newly created would be addressed by the new hash, but inside
 such an object (e.g. commit) it is still possible to address objects
 using the old hash function.
+
 * You cannot trust its history (needed for bisectability) in the
   future without further work
 * Maintenance burden as the number of supported hash functions grows
@@ -743,36 +740,38 @@
 Signed objects with multiple hashes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Instead of introducing the gpgsig-sha256 field in commit and tag objects
-for sha256-content based signatures, an earlier version of this design
-added "hash sha256 <sha256-name>" fields to strengthen the existing
-sha1-content based signatures.
+for SHA-256 content based signatures, an earlier version of this design
+added "hash sha256 <SHA-256 name>" fields to strengthen the existing
+SHA-1 content based signatures.
 
 In other words, a single signature was used to attest to the object
 content using both hash functions. This had some advantages:
+
 * Using one signature instead of two speeds up the signing process.
 * Having one signed payload with both hashes allows the signer to
-  attest to the sha1-name and sha256-name referring to the same object.
+  attest to the SHA-1 name and SHA-256 name referring to the same object.
 * All users consume the same signature. Broken signatures are likely
   to be detected quickly using current versions of git.
 
 However, it also came with disadvantages:
-* Verifying a signed object requires access to the sha1-names of all
+
+* Verifying a signed object requires access to the SHA-1 names of all
   objects it references, even after the transition is complete and
   translation table is no longer needed for anything else. To support
-  this, the design added fields such as "hash sha1 tree <sha1-name>"
-  and "hash sha1 parent <sha1-name>" to the sha256-content of a signed
+  this, the design added fields such as "hash sha1 tree <SHA-1 name>"
+  and "hash sha1 parent <SHA-1 name>" to the SHA-256 content of a signed
   commit, complicating the conversion process.
-* Allowing signed objects without a sha1 (for after the transition is
+* Allowing signed objects without a SHA-1 (for after the transition is
   complete) complicated the design further, requiring a "nohash sha1"
-  field to suppress including "hash sha1" fields in the sha256-content
+  field to suppress including "hash sha1" fields in the SHA-256 content
   and signed payload.
 
 Lazily populated translation table
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Some of the work of building the translation table could be deferred to
 push time, but that would significantly complicate and slow down pushes.
-Calculating the sha1-name at object creation time at the same time it is
-being streamed to disk and having its sha256-name calculated should be
+Calculating the SHA-1 name at object creation time at the same time it is
+being streamed to disk and having its SHA-256 name calculated should be
 an acceptable cost.
 
 Document History
@@ -782,18 +781,19 @@
 bmwill@google.com, jonathantanmy@google.com, jrnieder@gmail.com,
 sbeller@google.com
 
-Initial version sent to
-http://lore.kernel.org/git/20170304011251.GA26789@aiede.mtv.corp.google.com
+* Initial version sent to https://lore.kernel.org/git/20170304011251.GA26789@aiede.mtv.corp.google.com
 
 2017-03-03 jrnieder@gmail.com
 Incorporated suggestions from jonathantanmy and sbeller:
-* describe purpose of signed objects with each hash type
-* redefine signed object verification using object content under the
+
+* Describe purpose of signed objects with each hash type
+* Redefine signed object verification using object content under the
   first hash function
 
 2017-03-06 jrnieder@gmail.com
+
 * Use SHA3-256 instead of SHA2 (thanks, Linus and brian m. carlson).[1][2]
-* Make sha3-based signatures a separate field, avoiding the need for
+* Make SHA3-based signatures a separate field, avoiding the need for
   "hash" and "nohash" fields (thanks to peff[3]).
 * Add a sorting phase to fetch (thanks to Junio for noticing the need
   for this).
@@ -805,23 +805,26 @@
   especially Junio).
 
 2017-09-27 jrnieder@gmail.com, sbeller@google.com
-* use placeholder NewHash instead of SHA3-256
-* describe criteria for picking a hash function.
-* include a transition plan (thanks especially to Brandon Williams
+
+* Use placeholder NewHash instead of SHA3-256
+* Describe criteria for picking a hash function.
+* Include a transition plan (thanks especially to Brandon Williams
   for fleshing these ideas out)
-* define the translation table (thanks, Shawn Pearce[5], Jonathan
+* Define the translation table (thanks, Shawn Pearce[5], Jonathan
   Tan, and Masaya Suzuki)
-* avoid loose object overhead by packing more aggressively in
+* Avoid loose object overhead by packing more aggressively in
   "git gc --auto"
 
 Later history:
 
- See the history of this file in git.git for the history of subsequent
- edits. This document history is no longer being maintained as it
- would now be superfluous to the commit log
+* See the history of this file in git.git for the history of subsequent
+  edits. This document history is no longer being maintained as it
+  would now be superfluous to the commit log
 
-[1] http://lore.kernel.org/git/CA+55aFzJtejiCjV0e43+9oR3QuJK2PiFiLQemytoLpyJWe6P9w@mail.gmail.com/
-[2] http://lore.kernel.org/git/CA+55aFz+gkAsDZ24zmePQuEs1XPS9BP_s8O7Q4wQ7LV7X5-oDA@mail.gmail.com/
-[3] http://lore.kernel.org/git/20170306084353.nrns455dvkdsfgo5@sigill.intra.peff.net/
-[4] http://lore.kernel.org/git/20170304224936.rqqtkdvfjgyezsht@genre.crustytoothpaste.net
-[5] https://lore.kernel.org/git/CAJo=hJtoX9=AyLHHpUJS7fueV9ciZ_MNpnEPHUz8Whui6g9F0A@mail.gmail.com/
+References:
+
+ [1] https://lore.kernel.org/git/CA+55aFzJtejiCjV0e43+9oR3QuJK2PiFiLQemytoLpyJWe6P9w@mail.gmail.com/
+ [2] https://lore.kernel.org/git/CA+55aFz+gkAsDZ24zmePQuEs1XPS9BP_s8O7Q4wQ7LV7X5-oDA@mail.gmail.com/
+ [3] https://lore.kernel.org/git/20170306084353.nrns455dvkdsfgo5@sigill.intra.peff.net/
+ [4] https://lore.kernel.org/git/20170304224936.rqqtkdvfjgyezsht@genre.crustytoothpaste.net
+ [5] https://lore.kernel.org/git/CAJo=hJtoX9=AyLHHpUJS7fueV9ciZ_MNpnEPHUz8Whui6g9F0A@mail.gmail.com/
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index 69edf46..d363a71 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -26,7 +26,7 @@
      Extensions are identified by signature. Optional extensions can
      be ignored if Git does not understand them.
 
-     Git currently supports cached tree and resolve undo extensions.
+     Git currently supports cache tree and resolve undo extensions.
 
      4-byte extension signature. If the first byte is 'A'..'Z' the
      extension is optional and can be ignored.
@@ -136,14 +136,35 @@
 
 == Extensions
 
-=== Cached tree
+=== Cache tree
 
-  Cached tree extension contains pre-computed hashes for trees that can
-  be derived from the index. It helps speed up tree object generation
-  from index for a new commit.
+  Since the index does not record entries for directories, the cache
+  entries cannot describe tree objects that already exist in the object
+  database for regions of the index that are unchanged from an existing
+  commit. The cache tree extension stores a recursive tree structure that
+  describes the trees that already exist and completely match sections of
+  the cache entries. This speeds up tree object generation from the index
+  for a new commit by only computing the trees that are "new" to that
+  commit. It also assists when comparing the index to another tree, such
+  as `HEAD^{tree}`, since sections of the index can be skipped when a tree
+  comparison demonstrates equality.
 
-  When a path is updated in index, the path must be invalidated and
-  removed from tree cache.
+  The recursive tree structure uses nodes that store a number of cache
+  entries, a list of subnodes, and an object ID (OID). The OID references
+  the existing tree for that node, if it is known to exist. The subnodes
+  correspond to subdirectories that themselves have cache tree nodes. The
+  number of cache entries corresponds to the number of cache entries in
+  the index that describe paths within that tree's directory.
+
+  The extension tracks the full directory structure in the cache tree
+  extension, but this is generally smaller than the full cache entry list.
+
+  When a path is updated in index, Git invalidates all nodes of the
+  recursive cache tree corresponding to the parent directories of that
+  path. We store these tree nodes as being "invalid" by using "-1" as the
+  number of cache entries. Invalid nodes still store a span of index
+  entries, allowing Git to focus its efforts when reconstructing a full
+  cache tree.
 
   The signature for this extension is { 'T', 'R', 'E', 'E' }.
 
@@ -174,7 +195,8 @@
   first entry represents the root level of the repository, followed by the
   first subtree--let's call this A--of the root level (with its name
   relative to the root level), followed by the first subtree of A (with
-  its name relative to A), ...
+  its name relative to A), and so on. The specified number of subtrees
+  indicates when the current level of the recursive stack is complete.
 
 === Resolve undo
 
@@ -251,14 +273,14 @@
   - Stat data of $GIT_DIR/info/exclude. See "Index entry" section from
     ctime field until "file size".
 
-  - Stat data of core.excludesfile
+  - Stat data of core.excludesFile
 
   - 32-bit dir_flags (see struct dir_struct)
 
   - Hash of $GIT_DIR/info/exclude. A null hash means the file
     does not exist.
 
-  - Hash of core.excludesfile. A null hash means the file does
+  - Hash of core.excludesFile. A null hash means the file does
     not exist.
 
   - NUL-terminated string of per-dir exclude file name. This usually
diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt
index 96d2fc5..1faa949 100644
--- a/Documentation/technical/pack-format.txt
+++ b/Documentation/technical/pack-format.txt
@@ -274,6 +274,26 @@
 
     Index checksum of all of the above.
 
+== pack-*.rev files have the format:
+
+  - A 4-byte magic number '0x52494458' ('RIDX').
+
+  - A 4-byte version identifier (= 1).
+
+  - A 4-byte hash function identifier (= 1 for SHA-1, 2 for SHA-256).
+
+  - A table of index positions (one per packed object, num_objects in
+    total, each a 4-byte unsigned integer in network order), sorted by
+    their corresponding offsets in the packfile.
+
+  - A trailer, containing a:
+
+    checksum of the corresponding packfile, and
+
+    a checksum of all of the above.
+
+All 4-byte numbers are in network order.
+
 == multi-pack-index (MIDX) files have the following format:
 
 The multi-pack-index files refer to multiple pack-files and loose objects.
@@ -316,6 +336,9 @@
 	    (Chunks are provided in file-order, so you can infer the length
 	    using the next chunk position if necessary.)
 
+	The CHUNK LOOKUP matches the table of contents from
+	link:technical/chunk-format.html[the chunk-based file format].
+
 	The remaining data in the body is described one chunk at a time, and
 	these chunks may be given in any order. Chunks are required unless
 	otherwise specified.
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 85daeb5..a7c806a 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -33,8 +33,8 @@
 
   * '0000' Flush Packet (flush-pkt) - indicates the end of a message
   * '0001' Delimiter Packet (delim-pkt) - separates sections of a message
-  * '0002' Message Packet (response-end-pkt) - indicates the end of a response
-    for stateless connections
+  * '0002' Response End Packet (response-end-pkt) - indicates the end of a
+    response for stateless connections
 
 Initial Client Request
 ----------------------
@@ -192,11 +192,20 @@
 	When specified, only references having a prefix matching one of
 	the provided prefixes are displayed.
 
+If the 'unborn' feature is advertised the following argument can be
+included in the client's request.
+
+    unborn
+	The server will send information about HEAD even if it is a symref
+	pointing to an unborn branch in the form "unborn HEAD
+	symref-target:<target>".
+
 The output of ls-refs is as follows:
 
     output = *ref
 	     flush-pkt
-    ref = PKT-LINE(obj-id SP refname *(SP ref-attribute) LF)
+    obj-id-or-unborn = (obj-id | "unborn")
+    ref = PKT-LINE(obj-id-or-unborn SP refname *(SP ref-attribute) LF)
     ref-attribute = (symref | peeled)
     symref = "symref-target:" symref-target
     peeled = "peeled:" obj-id
diff --git a/Documentation/technical/reftable.txt b/Documentation/technical/reftable.txt
index 8095ab2..3ef169a 100644
--- a/Documentation/technical/reftable.txt
+++ b/Documentation/technical/reftable.txt
@@ -872,17 +872,11 @@
 Layout
 ^^^^^^
 
-A collection of reftable files are stored in the `$GIT_DIR/reftable/`
-directory:
-
-....
-00000001-00000001.log
-00000002-00000002.ref
-00000003-00000003.ref
-....
-
-where reftable files are named by a unique name such as produced by the
-function `${min_update_index}-${max_update_index}.ref`.
+A collection of reftable files are stored in the `$GIT_DIR/reftable/` directory.
+Their names should have a random element, such that each filename is globally
+unique; this helps avoid spurious failures on Windows, where open files cannot
+be removed or overwritten. It suggested to use
+`${min_update_index}-${max_update_index}-${random}.ref` as a naming convention.
 
 Log-only files use the `.log` extension, while ref-only and mixed ref
 and log files use `.ref`. extension.
@@ -893,9 +887,9 @@
 
 ....
 $ cat .git/reftable/tables.list
-00000001-00000001.log
-00000002-00000002.ref
-00000003-00000003.ref
+00000001-00000001-RANDOM1.log
+00000002-00000002-RANDOM2.ref
+00000003-00000003-RANDOM3.ref
 ....
 
 Readers must read `$GIT_DIR/reftable/tables.list` to determine which
@@ -940,7 +934,7 @@
 3.  Select `update_index` to be most recent file's
 `max_update_index + 1`.
 4.  Prepare temp reftable `tmp_XXXXXX`, including log entries.
-5.  Rename `tmp_XXXXXX` to `${update_index}-${update_index}.ref`.
+5.  Rename `tmp_XXXXXX` to `${update_index}-${update_index}-${random}.ref`.
 6.  Copy `tables.list` to `tables.list.lock`, appending file from (5).
 7.  Rename `tables.list.lock` to `tables.list`.
 
@@ -993,7 +987,7 @@
 should always be the case, assuming that other processes are adhering to
 the locking protocol.
 7.  Rename `${min_update_index}-${max_update_index}_XXXXXX` to
-`${min_update_index}-${max_update_index}.ref`.
+`${min_update_index}-${max_update_index}-${random}.ref`.
 8.  Write the new stack to `tables.list.lock`, replacing `B` and `C`
 with the file from (4).
 9.  Rename `tables.list.lock` to `tables.list`.
@@ -1005,6 +999,22 @@
 Each reftable (compacted or not) is uniquely identified by its name, so
 open reftables can be cached by their name.
 
+Windows
+^^^^^^^
+
+On windows, and other systems that do not allow deleting or renaming to open
+files, compaction may succeed, but other readers may prevent obsolete tables
+from being deleted.
+
+On these platforms, the following strategy can be followed: on closing a
+reftable stack, reload `tables.list`, and delete any tables no longer mentioned
+in `tables.list`.
+
+Irregular program exit may still leave about unused files. In this case, a
+cleanup operation can read `tables.list`, note its modification timestamp, and
+delete any unreferenced `*.ref` files that are older.
+
+
 Alternatives considered
 ~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 9d789e0..7b99fe5 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.30.2
+DEF_VER=v2.31.0-rc1
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 4128b45..dfb0f10 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,9 @@
 # when attempting to read from an fopen'ed directory (or even to fopen
 # it at all).
 #
+# Define OPEN_RETURNS_EINTR if your open() system call may return EINTR
+# when a signal is received (as opposed to restarting).
+#
 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 #
 # Define USE_LIBPCRE if you have and want to use libpcre. Various
@@ -29,18 +32,11 @@
 # Perl-compatible regular expressions instead of standard or extended
 # POSIX regular expressions.
 #
-# USE_LIBPCRE is a synonym for USE_LIBPCRE2, define USE_LIBPCRE1
-# instead if you'd like to use the legacy version 1 of the PCRE
-# library. Support for version 1 will likely be removed in some future
-# release of Git, as upstream has all but abandoned it.
-#
-# When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if you want to
-# disable JIT even if supported by your library.
+# Only libpcre version 2 is supported. USE_LIBPCRE2 is a synonym for
+# USE_LIBPCRE, support for the old USE_LIBPCRE1 has been removed.
 #
 # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
-# in /foo/bar/include and /foo/bar/lib directories. Which version of
-# PCRE this points to determined by the USE_LIBPCRE1 and USE_LIBPCRE2
-# variables.
+# in /foo/bar/include and /foo/bar/lib directories.
 #
 # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header.
 #
@@ -722,6 +718,7 @@
 TEST_BUILTINS_OBJS += test-parse-options.o
 TEST_BUILTINS_OBJS += test-parse-pathspec-file.o
 TEST_BUILTINS_OBJS += test-path-utils.o
+TEST_BUILTINS_OBJS += test-pcre2-config.o
 TEST_BUILTINS_OBJS += test-pkt-line.o
 TEST_BUILTINS_OBJS += test-prio-queue.o
 TEST_BUILTINS_OBJS += test-proc-receive.o
@@ -840,6 +837,7 @@
 LIB_OBJS += cache-tree.o
 LIB_OBJS += chdir-notify.o
 LIB_OBJS += checkout.o
+LIB_OBJS += chunk-format.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
@@ -860,6 +858,7 @@
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
 LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-merges.o
 LIB_OBJS += diff-lib.o
 LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff.o
@@ -868,6 +867,7 @@
 LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
+LIB_OBJS += diffcore-rotate.o
 LIB_OBJS += dir-iterator.o
 LIB_OBJS += dir.o
 LIB_OBJS += editor.o
@@ -887,6 +887,7 @@
 LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
+LIB_OBJS += hash-lookup.o
 LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
@@ -923,6 +924,8 @@
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += notes.o
+LIB_OBJS += object-file.o
+LIB_OBJS += object-name.o
 LIB_OBJS += object.o
 LIB_OBJS += oid-array.o
 LIB_OBJS += oidmap.o
@@ -979,9 +982,6 @@
 LIB_OBJS += serve.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
-LIB_OBJS += sha1-file.o
-LIB_OBJS += sha1-lookup.o
-LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
@@ -1359,26 +1359,17 @@
 	COMPAT_OBJS += compat/basename.o
 endif
 
+ifdef USE_LIBPCRE1
+$(error The USE_LIBPCRE1 build option has been removed, use version 2 with USE_LIBPCRE)
+endif
+
 USE_LIBPCRE2 ?= $(USE_LIBPCRE)
 
 ifneq (,$(USE_LIBPCRE2))
-	ifdef USE_LIBPCRE1
-$(error Only set USE_LIBPCRE2 (or its alias USE_LIBPCRE) or USE_LIBPCRE1, not both!)
-	endif
-
 	BASIC_CFLAGS += -DUSE_LIBPCRE2
 	EXTLIBS += -lpcre2-8
 endif
 
-ifdef USE_LIBPCRE1
-	BASIC_CFLAGS += -DUSE_LIBPCRE1
-	EXTLIBS += -lpcre
-
-ifdef NO_LIBPCRE1_JIT
-	BASIC_CFLAGS += -DNO_LIBPCRE1_JIT
-endif
-endif
-
 ifdef LIBPCREDIR
 	BASIC_CFLAGS += -I$(LIBPCREDIR)/include
 	EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
@@ -1551,6 +1542,10 @@
 	COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
 	COMPAT_OBJS += compat/fopen.o
 endif
+ifdef OPEN_RETURNS_EINTR
+	COMPAT_CFLAGS += -DOPEN_RETURNS_EINTR
+	COMPAT_OBJS += compat/open.o
+endif
 ifdef NO_SYMLINK_HEAD
 	BASIC_CFLAGS += -DNO_SYMLINK_HEAD
 endif
@@ -2726,9 +2721,7 @@
 	@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
 	@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
 	@echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
-	@echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE1)))'\' >>$@+
 	@echo USE_LIBPCRE2=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE2)))'\' >>$@+
-	@echo NO_LIBPCRE1_JIT=\''$(subst ','\'',$(subst ','\'',$(NO_LIBPCRE1_JIT)))'\' >>$@+
 	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
 	@echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
 	@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
diff --git a/RelNotes b/RelNotes
index d73be92..3324fc0 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.30.2.txt
\ No newline at end of file
+Documentation/RelNotes/2.31.0.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 6f15a41..39e06b5 100644
--- a/abspath.c
+++ b/abspath.c
@@ -67,19 +67,15 @@
 #endif
 
 /*
- * Return the real path (i.e., absolute path, with symlinks resolved
- * and extra slashes removed) equivalent to the specified path.  (If
- * you want an absolute path but don't mind links, use
- * absolute_path().)  Places the resolved realpath in the provided strbuf.
- *
- * The directory part of path (i.e., everything up to the last
- * dir_sep) must denote a valid, existing directory, but the last
- * component need not exist.  If die_on_error is set, then die with an
- * informative error message if there is a problem.  Otherwise, return
- * NULL on errors (without generating any output).
+ * If set, any number of trailing components may be missing; otherwise, only one
+ * may be.
  */
-char *strbuf_realpath(struct strbuf *resolved, const char *path,
-		      int die_on_error)
+#define REALPATH_MANY_MISSING (1 << 0)
+/* Should we die if there's an error? */
+#define REALPATH_DIE_ON_ERROR (1 << 1)
+
+static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
+			       int flags)
 {
 	struct strbuf remaining = STRBUF_INIT;
 	struct strbuf next = STRBUF_INIT;
@@ -89,7 +85,7 @@
 	struct stat st;
 
 	if (!*path) {
-		if (die_on_error)
+		if (flags & REALPATH_DIE_ON_ERROR)
 			die("The empty string is not a valid path");
 		else
 			goto error_out;
@@ -101,7 +97,7 @@
 	if (!resolved->len) {
 		/* relative path; can use CWD as the initial resolved path */
 		if (strbuf_getcwd(resolved)) {
-			if (die_on_error)
+			if (flags & REALPATH_DIE_ON_ERROR)
 				die_errno("unable to get current working directory");
 			else
 				goto error_out;
@@ -129,8 +125,9 @@
 
 		if (lstat(resolved->buf, &st)) {
 			/* error out unless this was the last component */
-			if (errno != ENOENT || remaining.len) {
-				if (die_on_error)
+			if (errno != ENOENT ||
+			   (!(flags & REALPATH_MANY_MISSING) && remaining.len)) {
+				if (flags & REALPATH_DIE_ON_ERROR)
 					die_errno("Invalid path '%s'",
 						  resolved->buf);
 				else
@@ -143,7 +140,7 @@
 			if (num_symlinks++ > MAXSYMLINKS) {
 				errno = ELOOP;
 
-				if (die_on_error)
+				if (flags & REALPATH_DIE_ON_ERROR)
 					die("More than %d nested symlinks "
 					    "on path '%s'", MAXSYMLINKS, path);
 				else
@@ -153,7 +150,7 @@
 			len = strbuf_readlink(&symlink, resolved->buf,
 					      st.st_size);
 			if (len < 0) {
-				if (die_on_error)
+				if (flags & REALPATH_DIE_ON_ERROR)
 					die_errno("Invalid symlink '%s'",
 						  resolved->buf);
 				else
@@ -202,6 +199,37 @@
 	return retval;
 }
 
+/*
+ * Return the real path (i.e., absolute path, with symlinks resolved
+ * and extra slashes removed) equivalent to the specified path.  (If
+ * you want an absolute path but don't mind links, use
+ * absolute_path().)  Places the resolved realpath in the provided strbuf.
+ *
+ * The directory part of path (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.  If die_on_error is set, then die with an
+ * informative error message if there is a problem.  Otherwise, return
+ * NULL on errors (without generating any output).
+ */
+char *strbuf_realpath(struct strbuf *resolved, const char *path,
+		      int die_on_error)
+{
+	return strbuf_realpath_1(resolved, path,
+				 die_on_error ? REALPATH_DIE_ON_ERROR : 0);
+}
+
+/*
+ * Just like strbuf_realpath, but allows an arbitrary number of path
+ * components to be missing.
+ */
+char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
+				int die_on_error)
+{
+	return strbuf_realpath_1(resolved, path,
+				 ((die_on_error ? REALPATH_DIE_ON_ERROR : 0) |
+				  REALPATH_MANY_MISSING));
+}
+
 char *real_pathdup(const char *path, int die_on_error)
 {
 	struct strbuf realpath = STRBUF_INIT;
diff --git a/bisect.c b/bisect.c
index d8c2c8f..ae48d19 100644
--- a/bisect.c
+++ b/bisect.c
@@ -6,7 +6,7 @@
 #include "refs.h"
 #include "list-objects.h"
 #include "quote.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
@@ -1064,7 +1064,7 @@
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
-			"Maybe you started with bad path parameters?\n"));
+			"Maybe you started with bad path arguments?\n"));
 
 		return BISECT_NO_TESTABLE_COMMIT;
 	}
diff --git a/builtin/add.c b/builtin/add.c
index a825887..ea762a4 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -38,19 +38,27 @@
 	int add_errors;
 };
 
-static void chmod_pathspec(struct pathspec *pathspec, char flip)
+static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
 {
-	int i;
+	int i, ret = 0;
 
 	for (i = 0; i < active_nr; i++) {
 		struct cache_entry *ce = active_cache[i];
+		int err;
 
 		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
 			continue;
 
-		if (chmod_cache_entry(ce, flip) < 0)
-			fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name);
+		if (!show_only)
+			err = chmod_cache_entry(ce, flip);
+		else
+			err = S_ISREG(ce->ce_mode) ? 0 : -1;
+
+		if (err < 0)
+			ret = error(_("cannot chmod %cx '%s'"), flip, ce->name);
 	}
+
+	return ret;
 }
 
 static int fix_unmerged_status(struct diff_filepair *p,
@@ -609,7 +617,7 @@
 		exit_status |= add_files(&dir, flags);
 
 	if (chmod_arg && pathspec.nr)
-		chmod_pathspec(&pathspec, chmod_arg[0]);
+		exit_status |= chmod_pathspec(&pathspec, chmod_arg[0], show_only);
 	unplug_bulk_checkin();
 
 finish:
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 709eb71..d69e133 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -21,16 +21,15 @@
 
 static const char * const git_bisect_helper_usage[] = {
 	N_("git bisect--helper --bisect-reset [<commit>]"),
-	N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
-	N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
 	N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
 	N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
 	N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
 					    " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
 	N_("git bisect--helper --bisect-next"),
-	N_("git bisect--helper --bisect-auto-next"),
 	N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
 	N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
+	N_("git bisect--helper --bisect-replay <filename>"),
+	N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
 	NULL
 };
 
@@ -904,28 +903,148 @@
 	return bisect_auto_next(terms, NULL);
 }
 
+static enum bisect_error bisect_log(void)
+{
+	int fd, status;
+	const char* filename = git_path_bisect_log();
+
+	if (is_empty_or_missing_file(filename))
+		return error(_("We are not bisecting."));
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return BISECT_FAILED;
+
+	status = copy_fd(fd, STDOUT_FILENO);
+	close(fd);
+	return status ? BISECT_FAILED : BISECT_OK;
+}
+
+static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
+{
+	const char *p = line->buf + strspn(line->buf, " \t");
+	char *word_end, *rev;
+
+	if ((!skip_prefix(p, "git bisect", &p) &&
+	!skip_prefix(p, "git-bisect", &p)) || !isspace(*p))
+		return 0;
+	p += strspn(p, " \t");
+
+	word_end = (char *)p + strcspn(p, " \t");
+	rev = word_end + strspn(word_end, " \t");
+	*word_end = '\0'; /* NUL-terminate the word */
+
+	get_terms(terms);
+	if (check_and_set_terms(terms, p))
+		return -1;
+
+	if (!strcmp(p, "start")) {
+		struct strvec argv = STRVEC_INIT;
+		int res;
+		sq_dequote_to_strvec(rev, &argv);
+		res = bisect_start(terms, argv.v, argv.nr);
+		strvec_clear(&argv);
+		return res;
+	}
+
+	if (one_of(p, terms->term_good,
+	   terms->term_bad, "skip", NULL))
+		return bisect_write(p, rev, terms, 0);
+
+	if (!strcmp(p, "terms")) {
+		struct strvec argv = STRVEC_INIT;
+		int res;
+		sq_dequote_to_strvec(rev, &argv);
+		res = bisect_terms(terms, argv.nr == 1 ? argv.v[0] : NULL);
+		strvec_clear(&argv);
+		return res;
+	}
+	error(_("'%s'?? what are you talking about?"), p);
+
+	return -1;
+}
+
+static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *filename)
+{
+	FILE *fp = NULL;
+	enum bisect_error res = BISECT_OK;
+	struct strbuf line = STRBUF_INIT;
+
+	if (is_empty_or_missing_file(filename))
+		return error(_("cannot read file '%s' for replaying"), filename);
+
+	if (bisect_reset(NULL))
+		return BISECT_FAILED;
+
+	fp = fopen(filename, "r");
+	if (!fp)
+		return BISECT_FAILED;
+
+	while ((strbuf_getline(&line, fp) != EOF) && !res)
+		res = process_replay_line(terms, &line);
+
+	strbuf_release(&line);
+	fclose(fp);
+
+	if (res)
+		return BISECT_FAILED;
+
+	return bisect_auto_next(terms, NULL);
+}
+
+static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+{
+	int i;
+	enum bisect_error res;
+	struct strvec argv_state = STRVEC_INIT;
+
+	strvec_push(&argv_state, "skip");
+
+	for (i = 0; i < argc; i++) {
+		const char *dotdot = strstr(argv[i], "..");
+
+		if (dotdot) {
+			struct rev_info revs;
+			struct commit *commit;
+
+			init_revisions(&revs, NULL);
+			setup_revisions(2, argv + i - 1, &revs, NULL);
+
+			if (prepare_revision_walk(&revs))
+				die(_("revision walk setup failed\n"));
+			while ((commit = get_revision(&revs)) != NULL)
+				strvec_push(&argv_state,
+						oid_to_hex(&commit->object.oid));
+
+			reset_revision_walk();
+		} else {
+			strvec_push(&argv_state, argv[i]);
+		}
+	}
+	res = bisect_state(terms, argv_state.v, argv_state.nr);
+
+	strvec_clear(&argv_state);
+	return res;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
 	enum {
 		BISECT_RESET = 1,
-		BISECT_WRITE,
-		CHECK_AND_SET_TERMS,
 		BISECT_NEXT_CHECK,
 		BISECT_TERMS,
 		BISECT_START,
 		BISECT_AUTOSTART,
 		BISECT_NEXT,
-		BISECT_AUTO_NEXT,
-		BISECT_STATE
+		BISECT_STATE,
+		BISECT_LOG,
+		BISECT_REPLAY,
+		BISECT_SKIP
 	} cmdmode = 0;
 	int res = 0, nolog = 0;
 	struct option options[] = {
 		OPT_CMDMODE(0, "bisect-reset", &cmdmode,
 			 N_("reset the bisection state"), BISECT_RESET),
-		OPT_CMDMODE(0, "bisect-write", &cmdmode,
-			 N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
-		OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
-			 N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
 		OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
 			 N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
 		OPT_CMDMODE(0, "bisect-terms", &cmdmode,
@@ -934,10 +1053,14 @@
 			 N_("start the bisect session"), BISECT_START),
 		OPT_CMDMODE(0, "bisect-next", &cmdmode,
 			 N_("find the next bisection commit"), BISECT_NEXT),
-		OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
-			 N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
 		OPT_CMDMODE(0, "bisect-state", &cmdmode,
 			 N_("mark the state of ref (or refs)"), BISECT_STATE),
+		OPT_CMDMODE(0, "bisect-log", &cmdmode,
+			 N_("list the bisection steps so far"), BISECT_LOG),
+		OPT_CMDMODE(0, "bisect-replay", &cmdmode,
+			 N_("replay the bisection process from the given file"), BISECT_REPLAY),
+		OPT_CMDMODE(0, "bisect-skip", &cmdmode,
+			 N_("skip some commits for checkout"), BISECT_SKIP),
 		OPT_BOOL(0, "no-log", &nolog,
 			 N_("no log for BISECT_WRITE")),
 		OPT_END()
@@ -955,18 +1078,7 @@
 	case BISECT_RESET:
 		if (argc > 1)
 			return error(_("--bisect-reset requires either no argument or a commit"));
-		return !!bisect_reset(argc ? argv[0] : NULL);
-	case BISECT_WRITE:
-		if (argc != 4 && argc != 5)
-			return error(_("--bisect-write requires either 4 or 5 arguments"));
-		set_terms(&terms, argv[3], argv[2]);
-		res = bisect_write(argv[0], argv[1], &terms, nolog);
-		break;
-	case CHECK_AND_SET_TERMS:
-		if (argc != 3)
-			return error(_("--check-and-set-terms requires 3 arguments"));
-		set_terms(&terms, argv[2], argv[1]);
-		res = check_and_set_terms(&terms, argv[0]);
+		res = bisect_reset(argc ? argv[0] : NULL);
 		break;
 	case BISECT_NEXT_CHECK:
 		if (argc != 2 && argc != 3)
@@ -989,17 +1101,26 @@
 		get_terms(&terms);
 		res = bisect_next(&terms, prefix);
 		break;
-	case BISECT_AUTO_NEXT:
-		if (argc)
-			return error(_("--bisect-auto-next requires 0 arguments"));
-		get_terms(&terms);
-		res = bisect_auto_next(&terms, prefix);
-		break;
 	case BISECT_STATE:
 		set_terms(&terms, "bad", "good");
 		get_terms(&terms);
 		res = bisect_state(&terms, argv, argc);
 		break;
+	case BISECT_LOG:
+		if (argc)
+			return error(_("--bisect-log requires 0 arguments"));
+		res = bisect_log();
+		break;
+	case BISECT_REPLAY:
+		if (argc != 1)
+			return error(_("no logfile given"));
+		set_terms(&terms, "bad", "good");
+		res = bisect_replay(&terms, argv[0]);
+		break;
+	case BISECT_SKIP:
+		set_terms(&terms, "bad", "good");
+		res = bisect_skip(&terms, argv, argc);
+		break;
 	default:
 		BUG("unknown subcommand %d", cmdmode);
 	}
diff --git a/builtin/blame.c b/builtin/blame.c
index 2c1c02c..641523f 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -425,13 +425,11 @@
 	parse_color_fields("blue,12 month ago,white,1 month ago,red");
 }
 
-static void determine_line_heat(struct blame_entry *ent, const char **dest_color)
+static void determine_line_heat(struct commit_info *ci, const char **dest_color)
 {
 	int i = 0;
-	struct commit_info ci;
-	get_commit_info(ent->suspect->commit, &ci, 1);
 
-	while (i < colorfield_nr && ci.author_time > colorfield[i].hop)
+	while (i < colorfield_nr && ci->author_time > colorfield[i].hop)
 		i++;
 
 	*dest_color = colorfield[i].col;
@@ -453,7 +451,7 @@
 	cp = blame_nth_line(sb, ent->lno);
 
 	if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) {
-		determine_line_heat(ent, &default_color);
+		determine_line_heat(&ci, &default_color);
 		color = default_color;
 		reset = GIT_COLOR_RESET;
 	}
@@ -1151,7 +1149,7 @@
 	sb.xdl_opts = xdl_opts;
 	sb.no_whole_file_rename = no_whole_file_rename;
 
-	read_mailmap(&mailmap, NULL);
+	read_mailmap(&mailmap);
 
 	sb.found_guilty_entry = &found_guilty_entry;
 	sb.found_guilty_entry_data = &pi;
diff --git a/builtin/branch.c b/builtin/branch.c
index 8c0b428..bcc00bc 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -202,6 +202,9 @@
 	int remote_branch = 0;
 	struct strbuf bname = STRBUF_INIT;
 	unsigned allowed_interpret;
+	struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	int branch_name_pos;
 
 	switch (kinds) {
 	case FILTER_REFS_REMOTES:
@@ -219,6 +222,7 @@
 	default:
 		die(_("cannot use -a with -d"));
 	}
+	branch_name_pos = strcspn(fmt, "%");
 
 	if (!force) {
 		head_rev = lookup_commit_reference(the_repository, &head_oid);
@@ -265,30 +269,35 @@
 			goto next;
 		}
 
-		if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
-			       REF_NO_DEREF)) {
-			error(remote_branch
-			      ? _("Error deleting remote-tracking branch '%s'")
-			      : _("Error deleting branch '%s'"),
-			      bname.buf);
-			ret = 1;
-			goto next;
-		}
-		if (!quiet) {
-			printf(remote_branch
-			       ? _("Deleted remote-tracking branch %s (was %s).\n")
-			       : _("Deleted branch %s (was %s).\n"),
-			       bname.buf,
-			       (flags & REF_ISBROKEN) ? "broken"
-			       : (flags & REF_ISSYMREF) ? target
-			       : find_unique_abbrev(&oid, DEFAULT_ABBREV));
-		}
-		delete_branch_config(bname.buf);
+		item = string_list_append(&refs_to_delete, name);
+		item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
+				    : (flags & REF_ISSYMREF) ? target
+				    : find_unique_abbrev(&oid, DEFAULT_ABBREV));
 
 	next:
 		free(target);
 	}
 
+	if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+		ret = 1;
+
+	for_each_string_list_item(item, &refs_to_delete) {
+		char *describe_ref = item->util;
+		char *name = item->string;
+		if (!ref_exists(name)) {
+			char *refname = name + branch_name_pos;
+			if (!quiet)
+				printf(remote_branch
+					? _("Deleted remote-tracking branch %s (was %s).\n")
+					: _("Deleted branch %s (was %s).\n"),
+					name + branch_name_pos, describe_ref);
+
+			delete_branch_config(refname);
+		}
+		free(describe_ref);
+	}
+	string_list_clear(&refs_to_delete, 0);
+
 	free(name);
 	strbuf_release(&bname);
 
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index cdce144..7dc47e4 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -47,7 +47,7 @@
 	if (argc == 0 && !use_stdin)
 		die(_("no contacts specified"));
 
-	read_mailmap(&mailmap, NULL);
+	read_mailmap(&mailmap);
 
 	for (i = 0; i < argc; ++i)
 		check_mailmap(&mailmap, argv[i]);
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 4bbfc92..023e49e 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -23,22 +23,35 @@
 static void write_tempfile_record(const char *name, const char *prefix)
 {
 	int i;
+	int have_tempname = 0;
 
 	if (CHECKOUT_ALL == checkout_stage) {
-		for (i = 1; i < 4; i++) {
-			if (i > 1)
-				putchar(' ');
-			if (topath[i][0])
-				fputs(topath[i], stdout);
-			else
-				putchar('.');
-		}
-	} else
-		fputs(topath[checkout_stage], stdout);
+		for (i = 1; i < 4; i++)
+			if (topath[i][0]) {
+				have_tempname = 1;
+				break;
+			}
 
-	putchar('\t');
-	write_name_quoted_relative(name, prefix, stdout,
-				   nul_term_line ? '\0' : '\n');
+		if (have_tempname) {
+			for (i = 1; i < 4; i++) {
+				if (i > 1)
+					putchar(' ');
+				if (topath[i][0])
+					fputs(topath[i], stdout);
+				else
+					putchar('.');
+			}
+		}
+	} else if (topath[checkout_stage][0]) {
+		have_tempname = 1;
+		fputs(topath[checkout_stage], stdout);
+	}
+
+	if (have_tempname) {
+		putchar('\t');
+		write_name_quoted_relative(name, prefix, stdout,
+					   nul_term_line ? '\0' : '\n');
+	}
 
 	for (i = 0; i < 4; i++) {
 		topath[i][0] = 0;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index c9ba23c..2d6550b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -821,9 +821,6 @@
 		}
 	}
 
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-
 	if (!cache_tree_fully_valid(active_cache_tree))
 		cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
diff --git a/builtin/clone.c b/builtin/clone.c
index e335734..51e844a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -979,7 +979,8 @@
 	int err = 0, complete_refs_before_fetch = 1;
 	int submodule_progress;
 
-	struct strvec ref_prefixes = STRVEC_INIT;
+	struct transport_ls_refs_options transport_ls_refs_options =
+		TRANSPORT_LS_REFS_OPTIONS_INIT;
 
 	packet_trace_identity("clone");
 
@@ -1257,14 +1258,17 @@
 		transport->smart_options->check_self_contained_and_connected = 1;
 
 
-	strvec_push(&ref_prefixes, "HEAD");
-	refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
+	strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+	refspec_ref_prefixes(&remote->fetch,
+			     &transport_ls_refs_options.ref_prefixes);
 	if (option_branch)
-		expand_ref_prefix(&ref_prefixes, option_branch);
+		expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
+				  option_branch);
 	if (!option_no_tags)
-		strvec_push(&ref_prefixes, "refs/tags/");
+		strvec_push(&transport_ls_refs_options.ref_prefixes,
+			    "refs/tags/");
 
-	refs = transport_get_remote_refs(transport, &ref_prefixes);
+	refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
 
 	if (refs) {
 		int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
@@ -1326,8 +1330,19 @@
 		remote_head = NULL;
 		option_no_checkout = 1;
 		if (!option_bare) {
-			const char *branch = git_default_branch_name(0);
-			char *ref = xstrfmt("refs/heads/%s", branch);
+			const char *branch;
+			char *ref;
+
+			if (transport_ls_refs_options.unborn_head_target &&
+			    skip_prefix(transport_ls_refs_options.unborn_head_target,
+					"refs/heads/", &branch)) {
+				ref = transport_ls_refs_options.unborn_head_target;
+				transport_ls_refs_options.unborn_head_target = NULL;
+				create_symref("HEAD", ref, reflog_msg.buf);
+			} else {
+				branch = git_default_branch_name(0);
+				ref = xstrfmt("refs/heads/%s", branch);
+			}
 
 			install_branch_config(0, branch, remote_name, ref);
 			free(ref);
@@ -1380,6 +1395,7 @@
 	strbuf_release(&key);
 	junk_mode = JUNK_LEAVE_ALL;
 
-	strvec_clear(&ref_prefixes);
+	strvec_clear(&transport_ls_refs_options.ref_prefixes);
+	free(transport_ls_refs_options.unborn_head_target);
 	return err;
 }
diff --git a/builtin/commit.c b/builtin/commit.c
index 505fe60..739110c 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1039,7 +1039,7 @@
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
 	revs.mailmap = &mailmap;
-	read_mailmap(revs.mailmap, NULL);
+	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
 		die(_("revision walk setup failed"));
diff --git a/builtin/describe.c b/builtin/describe.c
index 7668591..40482d8 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -194,7 +194,7 @@
 	}
 
 	/* Is it annotated? */
-	if (!peel_ref(path, &peeled)) {
+	if (!peel_iterated_oid(oid, &peeled)) {
 		is_annotated = !oideq(oid, &peeled);
 	} else {
 		oidcpy(&peeled, oid);
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 1e352dd..70103c4 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -7,6 +7,7 @@
 #include "cache.h"
 #include "config.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
@@ -35,7 +36,7 @@
 	 */
 	rev.diffopt.ita_invisible_in_index = 1;
 
-	precompose_argv(argc, argv);
+	prefix = precompose_argv_prefix(argc, argv, prefix);
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
 	while (1 < argc && argv[1][0] == '-') {
@@ -53,6 +54,7 @@
 	}
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+	rev.diffopt.rotate_to_strict = 1;
 
 	/*
 	 * Make sure there are NO revision (i.e. pending object) parameter,
@@ -69,9 +71,9 @@
 	 * was not asked to.  "diff-files -c -p" should not densify
 	 * (the user should ask with "diff-files --cc" explicitly).
 	 */
-	if (rev.max_count == -1 && !rev.combine_merges &&
+	if (rev.max_count == -1 &&
 	    (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
-		rev.combine_merges = rev.dense_combined_merges = 1;
+		diff_merges_set_dense_combined_if_unset(&rev);
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 7f5281c..176fe7f 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -25,7 +25,7 @@
 	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 	repo_init_revisions(the_repository, &rev, prefix);
 	rev.abbrev = 0;
-	precompose_argv(argc, argv);
+	prefix = precompose_argv_prefix(argc, argv, prefix);
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
 	for (i = 1; i < argc; i++) {
@@ -41,6 +41,8 @@
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
 
+	rev.diffopt.rotate_to_strict = 1;
+
 	/*
 	 * Make sure there is one revision (i.e. pending object),
 	 * and there is no revision filtering parameters.
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 9fc95e9..f33d30d 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -126,7 +126,7 @@
 	memset(&s_r_opt, 0, sizeof(s_r_opt));
 	s_r_opt.tweak = diff_tree_tweak_rev;
 
-	precompose_argv(argc, argv);
+	prefix = precompose_argv_prefix(argc, argv, prefix);
 	argc = setup_revisions(argc, argv, opt, &s_r_opt);
 
 	memset(&w, 0, sizeof(w));
@@ -156,6 +156,8 @@
 	if (merge_base && opt->pending.nr != 2)
 		die(_("--merge-base only works with two commits"));
 
+	opt->diffopt.rotate_to_strict = 1;
+
 	/*
 	 * NOTE!  We expect "a..b" to expand to "^a b" but it is
 	 * perfectly valid for revision range parser to yield "b ^a",
@@ -192,6 +194,7 @@
 		int saved_nrl = 0;
 		int saved_dcctc = 0;
 
+		opt->diffopt.rotate_to_strict = 0;
 		if (opt->diffopt.detect_rename) {
 			if (!the_index.cache)
 				repo_read_index(the_repository);
diff --git a/builtin/diff.c b/builtin/diff.c
index 780c338..617b9a4 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -13,6 +13,7 @@
 #include "blob.h"
 #include "tag.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "diffcore.h"
 #include "revision.h"
 #include "log-tree.h"
@@ -216,8 +217,8 @@
 	if (argc > 1)
 		usage(builtin_diff_usage);
 
-	if (!revs->dense_combined_merges && !revs->combine_merges)
-		revs->dense_combined_merges = revs->combine_merges = 1;
+	diff_merges_set_dense_combined_if_unset(revs);
+
 	for (i = 1; i < ents; i++)
 		oid_array_append(&parents, &ent[i].item->oid);
 	diff_tree_combined(&ent[0].item->oid, &parents, revs);
@@ -265,9 +266,9 @@
 	 * dense one, --cc can be explicitly asked for, or just rely
 	 * on the default).
 	 */
-	if (revs->max_count == -1 && !revs->combine_merges &&
+	if (revs->max_count == -1 &&
 	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
-		revs->combine_merges = revs->dense_combined_merges = 1;
+		diff_merges_set_dense_combined_if_unset(revs);
 
 	setup_work_tree();
 	if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
@@ -452,7 +453,7 @@
 
 	init_diff_ui_defaults();
 	git_config(git_diff_ui_config, NULL);
-	precompose_argv(argc, argv);
+	prefix = precompose_argv_prefix(argc, argv, prefix);
 
 	repo_init_revisions(the_repository, &rev, prefix);
 
@@ -490,6 +491,7 @@
 	}
 
 	rev.diffopt.flags.recursive = 1;
+	rev.diffopt.rotate_to_strict = 1;
 
 	setup_diff_pager(&rev.diffopt);
 
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 58b7c1f..c2d96f4 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -220,7 +220,8 @@
 	version = discover_version(&reader);
 	switch (version) {
 	case protocol_v2:
-		get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL, args.stateless_rpc);
+		get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL,
+				args.stateless_rpc);
 		break;
 	case protocol_v1:
 	case protocol_v0:
diff --git a/builtin/fetch.c b/builtin/fetch.c
index ecf8537..0b90de8 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -63,6 +63,7 @@
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
 static int max_jobs = -1, submodule_fetch_jobs_config = -1;
 static int fetch_parallel_config = 1;
+static int atomic_fetch;
 static enum transport_family family;
 static const char *depth;
 static const char *deepen_since;
@@ -144,6 +145,8 @@
 		 N_("set upstream for git pull/fetch")),
 	OPT_BOOL('a', "append", &append,
 		 N_("append to .git/FETCH_HEAD instead of overwriting")),
+	OPT_BOOL(0, "atomic", &atomic_fetch,
+		 N_("use atomic transaction to update references")),
 	OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
 		   N_("path to upload pack on remote end")),
 	OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
@@ -583,13 +586,14 @@
 
 static int s_update_ref(const char *action,
 			struct ref *ref,
+			struct ref_transaction *transaction,
 			int check_old)
 {
 	char *msg;
 	char *rla = getenv("GIT_REFLOG_ACTION");
-	struct ref_transaction *transaction;
+	struct ref_transaction *our_transaction = NULL;
 	struct strbuf err = STRBUF_INIT;
-	int ret, df_conflict = 0;
+	int ret;
 
 	if (dry_run)
 		return 0;
@@ -597,31 +601,47 @@
 		rla = default_rla.buf;
 	msg = xstrfmt("%s: %s", rla, action);
 
-	transaction = ref_transaction_begin(&err);
-	if (!transaction ||
-	    ref_transaction_update(transaction, ref->name,
-				   &ref->new_oid,
-				   check_old ? &ref->old_oid : NULL,
-				   0, msg, &err))
-		goto fail;
-
-	ret = ref_transaction_commit(transaction, &err);
-	if (ret) {
-		df_conflict = (ret == TRANSACTION_NAME_CONFLICT);
-		goto fail;
+	/*
+	 * If no transaction was passed to us, we manage the transaction
+	 * ourselves. Otherwise, we trust the caller to handle the transaction
+	 * lifecycle.
+	 */
+	if (!transaction) {
+		transaction = our_transaction = ref_transaction_begin(&err);
+		if (!transaction) {
+			ret = STORE_REF_ERROR_OTHER;
+			goto out;
+		}
 	}
 
-	ref_transaction_free(transaction);
+	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
+				     check_old ? &ref->old_oid : NULL,
+				     0, msg, &err);
+	if (ret) {
+		ret = STORE_REF_ERROR_OTHER;
+		goto out;
+	}
+
+	if (our_transaction) {
+		switch (ref_transaction_commit(our_transaction, &err)) {
+		case 0:
+			break;
+		case TRANSACTION_NAME_CONFLICT:
+			ret = STORE_REF_ERROR_DF_CONFLICT;
+			goto out;
+		default:
+			ret = STORE_REF_ERROR_OTHER;
+			goto out;
+		}
+	}
+
+out:
+	ref_transaction_free(our_transaction);
+	if (ret)
+		error("%s", err.buf);
 	strbuf_release(&err);
 	free(msg);
-	return 0;
-fail:
-	ref_transaction_free(transaction);
-	error("%s", err.buf);
-	strbuf_release(&err);
-	free(msg);
-	return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
-			   : STORE_REF_ERROR_OTHER;
+	return ret;
 }
 
 static int refcol_width = 10;
@@ -759,6 +779,7 @@
 }
 
 static int update_local_ref(struct ref *ref,
+			    struct ref_transaction *transaction,
 			    const char *remote,
 			    const struct ref *remote_ref,
 			    struct strbuf *display,
@@ -799,7 +820,7 @@
 	    starts_with(ref->name, "refs/tags/")) {
 		if (force || ref->force) {
 			int r;
-			r = s_update_ref("updating tag", ref, 0);
+			r = s_update_ref("updating tag", ref, transaction, 0);
 			format_display(display, r ? '!' : 't', _("[tag update]"),
 				       r ? _("unable to update local ref") : NULL,
 				       remote, pretty_ref, summary_width);
@@ -836,7 +857,7 @@
 			what = _("[new ref]");
 		}
 
-		r = s_update_ref(msg, ref, 0);
+		r = s_update_ref(msg, ref, transaction, 0);
 		format_display(display, r ? '!' : '*', what,
 			       r ? _("unable to update local ref") : NULL,
 			       remote, pretty_ref, summary_width);
@@ -858,7 +879,7 @@
 		strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
 		strbuf_addstr(&quickref, "..");
 		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-		r = s_update_ref("fast-forward", ref, 1);
+		r = s_update_ref("fast-forward", ref, transaction, 1);
 		format_display(display, r ? '!' : ' ', quickref.buf,
 			       r ? _("unable to update local ref") : NULL,
 			       remote, pretty_ref, summary_width);
@@ -870,7 +891,7 @@
 		strbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);
 		strbuf_addstr(&quickref, "...");
 		strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
-		r = s_update_ref("forced-update", ref, 1);
+		r = s_update_ref("forced-update", ref, transaction, 1);
 		format_display(display, r ? '!' : '+', quickref.buf,
 			       r ? _("unable to update local ref") : _("forced update"),
 			       remote, pretty_ref, summary_width);
@@ -897,6 +918,89 @@
 	return 0;
 }
 
+struct fetch_head {
+	FILE *fp;
+	struct strbuf buf;
+};
+
+static int open_fetch_head(struct fetch_head *fetch_head)
+{
+	const char *filename = git_path_fetch_head(the_repository);
+
+	if (write_fetch_head) {
+		fetch_head->fp = fopen(filename, "a");
+		if (!fetch_head->fp)
+			return error_errno(_("cannot open %s"), filename);
+		strbuf_init(&fetch_head->buf, 0);
+	} else {
+		fetch_head->fp = NULL;
+	}
+
+	return 0;
+}
+
+static void append_fetch_head(struct fetch_head *fetch_head,
+			      const struct object_id *old_oid,
+			      enum fetch_head_status fetch_head_status,
+			      const char *note,
+			      const char *url, size_t url_len)
+{
+	char old_oid_hex[GIT_MAX_HEXSZ + 1];
+	const char *merge_status_marker;
+	size_t i;
+
+	if (!fetch_head->fp)
+		return;
+
+	switch (fetch_head_status) {
+	case FETCH_HEAD_NOT_FOR_MERGE:
+		merge_status_marker = "not-for-merge";
+		break;
+	case FETCH_HEAD_MERGE:
+		merge_status_marker = "";
+		break;
+	default:
+		/* do not write anything to FETCH_HEAD */
+		return;
+	}
+
+	strbuf_addf(&fetch_head->buf, "%s\t%s\t%s",
+		    oid_to_hex_r(old_oid_hex, old_oid), merge_status_marker, note);
+	for (i = 0; i < url_len; ++i)
+		if ('\n' == url[i])
+			strbuf_addstr(&fetch_head->buf, "\\n");
+		else
+			strbuf_addch(&fetch_head->buf, url[i]);
+	strbuf_addch(&fetch_head->buf, '\n');
+
+	/*
+	 * When using an atomic fetch, we do not want to update FETCH_HEAD if
+	 * any of the reference updates fails. We thus have to write all
+	 * updates to a buffer first and only commit it as soon as all
+	 * references have been successfully updated.
+	 */
+	if (!atomic_fetch) {
+		strbuf_write(&fetch_head->buf, fetch_head->fp);
+		strbuf_reset(&fetch_head->buf);
+	}
+}
+
+static void commit_fetch_head(struct fetch_head *fetch_head)
+{
+	if (!fetch_head->fp || !atomic_fetch)
+		return;
+	strbuf_write(&fetch_head->buf, fetch_head->fp);
+}
+
+static void close_fetch_head(struct fetch_head *fetch_head)
+{
+	if (!fetch_head->fp)
+		return;
+
+	fclose(fetch_head->fp);
+	strbuf_release(&fetch_head->buf);
+}
+
 static const char warn_show_forced_updates[] =
 N_("Fetch normally indicates which branches had a forced update,\n"
    "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
@@ -909,22 +1013,20 @@
 static int store_updated_refs(const char *raw_url, const char *remote_name,
 			      int connectivity_checked, struct ref *ref_map)
 {
-	FILE *fp;
+	struct fetch_head fetch_head;
 	struct commit *commit;
 	int url_len, i, rc = 0;
-	struct strbuf note = STRBUF_INIT;
+	struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
+	struct ref_transaction *transaction = NULL;
 	const char *what, *kind;
 	struct ref *rm;
 	char *url;
-	const char *filename = (!write_fetch_head
-				? "/dev/null"
-				: git_path_fetch_head(the_repository));
 	int want_status;
 	int summary_width = transport_summary_width(ref_map);
 
-	fp = fopen(filename, "a");
-	if (!fp)
-		return error_errno(_("cannot open %s"), filename);
+	rc = open_fetch_head(&fetch_head);
+	if (rc)
+		return -1;
 
 	if (raw_url)
 		url = transport_anonymize_url(raw_url);
@@ -941,6 +1043,14 @@
 		}
 	}
 
+	if (atomic_fetch) {
+		transaction = ref_transaction_begin(&err);
+		if (!transaction) {
+			error("%s", err.buf);
+			goto abort;
+		}
+	}
+
 	prepare_format_display(ref_map);
 
 	/*
@@ -953,7 +1063,6 @@
 	     want_status++) {
 		for (rm = ref_map; rm; rm = rm->next) {
 			struct ref *ref = NULL;
-			const char *merge_status_marker = "";
 
 			if (rm->status == REF_STATUS_REJECT_SHALLOW) {
 				if (want_status == FETCH_HEAD_MERGE)
@@ -1011,31 +1120,15 @@
 					strbuf_addf(&note, "%s ", kind);
 				strbuf_addf(&note, "'%s' of ", what);
 			}
-			switch (rm->fetch_head_status) {
-			case FETCH_HEAD_NOT_FOR_MERGE:
-				merge_status_marker = "not-for-merge";
-				/* fall-through */
-			case FETCH_HEAD_MERGE:
-				fprintf(fp, "%s\t%s\t%s",
-					oid_to_hex(&rm->old_oid),
-					merge_status_marker,
-					note.buf);
-				for (i = 0; i < url_len; ++i)
-					if ('\n' == url[i])
-						fputs("\\n", fp);
-					else
-						fputc(url[i], fp);
-				fputc('\n', fp);
-				break;
-			default:
-				/* do not write anything to FETCH_HEAD */
-				break;
-			}
+
+			append_fetch_head(&fetch_head, &rm->old_oid,
+					  rm->fetch_head_status,
+					  note.buf, url, url_len);
 
 			strbuf_reset(&note);
 			if (ref) {
-				rc |= update_local_ref(ref, what, rm, &note,
-						       summary_width);
+				rc |= update_local_ref(ref, transaction, what,
+						       rm, &note, summary_width);
 				free(ref);
 			} else if (write_fetch_head || dry_run) {
 				/*
@@ -1060,6 +1153,17 @@
 		}
 	}
 
+	if (!rc && transaction) {
+		rc = ref_transaction_commit(transaction, &err);
+		if (rc) {
+			error("%s", err.buf);
+			goto abort;
+		}
+	}
+
+	if (!rc)
+		commit_fetch_head(&fetch_head);
+
 	if (rc & STORE_REF_ERROR_DF_CONFLICT)
 		error(_("some local refs could not be updated; try running\n"
 		      " 'git remote prune %s' to remove any old, conflicting "
@@ -1076,8 +1180,10 @@
 
  abort:
 	strbuf_release(&note);
+	strbuf_release(&err);
+	ref_transaction_free(transaction);
 	free(url);
-	fclose(fp);
+	close_fetch_head(&fetch_head);
 	return rc;
 }
 
@@ -1349,7 +1455,8 @@
 	int autotags = (transport->remote->fetch_tags == 1);
 	int retcode = 0;
 	const struct ref *remote_refs;
-	struct strvec ref_prefixes = STRVEC_INIT;
+	struct transport_ls_refs_options transport_ls_refs_options =
+		TRANSPORT_LS_REFS_OPTIONS_INIT;
 	int must_list_refs = 1;
 
 	if (tags == TAGS_DEFAULT) {
@@ -1369,7 +1476,7 @@
 	if (rs->nr) {
 		int i;
 
-		refspec_ref_prefixes(rs, &ref_prefixes);
+		refspec_ref_prefixes(rs, &transport_ls_refs_options.ref_prefixes);
 
 		/*
 		 * We can avoid listing refs if all of them are exact
@@ -1383,22 +1490,25 @@
 			}
 		}
 	} else if (transport->remote && transport->remote->fetch.nr)
-		refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
+		refspec_ref_prefixes(&transport->remote->fetch,
+				     &transport_ls_refs_options.ref_prefixes);
 
 	if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
 		must_list_refs = 1;
-		if (ref_prefixes.nr)
-			strvec_push(&ref_prefixes, "refs/tags/");
+		if (transport_ls_refs_options.ref_prefixes.nr)
+			strvec_push(&transport_ls_refs_options.ref_prefixes,
+				    "refs/tags/");
 	}
 
 	if (must_list_refs) {
 		trace2_region_enter("fetch", "remote_refs", the_repository);
-		remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+		remote_refs = transport_get_remote_refs(transport,
+							&transport_ls_refs_options);
 		trace2_region_leave("fetch", "remote_refs", the_repository);
 	} else
 		remote_refs = NULL;
 
-	strvec_clear(&ref_prefixes);
+	strvec_clear(&transport_ls_refs_options.ref_prefixes);
 
 	ref_map = get_ref_map(transport->remote, remote_refs, rs,
 			      tags, &autotags);
@@ -1887,6 +1997,10 @@
 			die(_("--filter can only be used with the remote "
 			      "configured in extensions.partialclone"));
 
+		if (atomic_fetch)
+			die(_("--atomic can only be used when fetching "
+			      "from one remote"));
+
 		if (stdin_refspecs)
 			die(_("--stdin can only be used when fetching "
 			      "from one remote"));
diff --git a/builtin/fsck.c b/builtin/fsck.c
index fbf26ca..821e779 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -73,25 +73,7 @@
 
 static int fsck_config(const char *var, const char *value, void *cb)
 {
-	if (strcmp(var, "fsck.skiplist") == 0) {
-		const char *path;
-		struct strbuf sb = STRBUF_INIT;
-
-		if (git_config_pathname(&path, var, value))
-			return 1;
-		strbuf_addf(&sb, "skiplist=%s", path);
-		free((char *)path);
-		fsck_set_msg_types(&fsck_obj_options, sb.buf);
-		strbuf_release(&sb);
-		return 0;
-	}
-
-	if (skip_prefix(var, "fsck.", &var)) {
-		fsck_set_msg_type(&fsck_obj_options, var, value);
-		return 0;
-	}
-
-	return git_default_config(var, value, cb);
+	return fsck_config_internal(var, value, cb, &fsck_obj_options);
 }
 
 static int objerror(struct object *obj, const char *err)
diff --git a/builtin/gc.c b/builtin/gc.c
index 64f2b52..ef7226d 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -54,7 +54,6 @@
 static unsigned long big_pack_threshold;
 static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
 
-static struct strvec pack_refs_cmd = STRVEC_INIT;
 static struct strvec reflog = STRVEC_INIT;
 static struct strvec repack = STRVEC_INIT;
 static struct strvec prune = STRVEC_INIT;
@@ -163,6 +162,15 @@
 	git_config(git_default_config, NULL);
 }
 
+struct maintenance_run_opts;
+static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
+{
+	struct strvec pack_refs_cmd = STRVEC_INIT;
+	strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+
+	return run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD);
+}
+
 static int too_many_loose_objects(void)
 {
 	/*
@@ -301,7 +309,7 @@
 	/* and then obj_hash[], underestimated in fact */
 	heap += sizeof(struct object *) * nr_objects;
 	/* revindex is used also */
-	heap += sizeof(struct revindex_entry) * nr_objects;
+	heap += (sizeof(off_t) + sizeof(uint32_t)) * nr_objects;
 	/*
 	 * read_sha1_file() (either at delta calculation phase, or
 	 * writing phase) also fills up the delta base cache
@@ -518,8 +526,8 @@
 	if (done++)
 		return;
 
-	if (pack_refs && run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD))
-		die(FAILED_RUN, pack_refs_cmd.v[0]);
+	if (pack_refs && maintenance_task_pack_refs(NULL))
+		die(FAILED_RUN, "pack-refs");
 
 	if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))
 		die(FAILED_RUN, reflog.v[0]);
@@ -556,7 +564,6 @@
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_gc_usage, builtin_gc_options);
 
-	strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
 	strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
 	strvec_pushl(&repack, "repack", "-d", "-l", NULL);
 	strvec_pushl(&prune, "prune", "--expire", NULL);
@@ -769,7 +776,7 @@
 	struct commit_list *stack = NULL;
 	struct commit *commit;
 
-	if (!peel_ref(refname, &peeled))
+	if (!peel_iterated_oid(oid, &peeled))
 		oid = &peeled;
 	if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
 		return 0;
@@ -897,6 +904,12 @@
 	struct string_list_item *item;
 	struct string_list remotes = STRING_LIST_INIT_DUP;
 
+	git_config_set_multivar_gently("log.excludedecoration",
+					"refs/prefetch/",
+					"refs/prefetch/",
+					CONFIG_FLAGS_FIXED_VALUE |
+					CONFIG_FLAGS_MULTI_REPLACE);
+
 	if (for_each_remote(append_remote, &remotes)) {
 		error(_("failed to fill remotes"));
 		result = 1;
@@ -1218,6 +1231,7 @@
 	TASK_INCREMENTAL_REPACK,
 	TASK_GC,
 	TASK_COMMIT_GRAPH,
+	TASK_PACK_REFS,
 
 	/* Leave as final value */
 	TASK__COUNT
@@ -1249,6 +1263,11 @@
 		maintenance_task_commit_graph,
 		should_write_commit_graph,
 	},
+	[TASK_PACK_REFS] = {
+		"pack-refs",
+		maintenance_task_pack_refs,
+		NULL,
+	},
 };
 
 static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1333,6 +1352,8 @@
 		tasks[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY;
 		tasks[TASK_LOOSE_OBJECTS].enabled = 1;
 		tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
+		tasks[TASK_PACK_REFS].enabled = 1;
+		tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
 	}
 }
 
@@ -1440,11 +1461,23 @@
 	return maintenance_run_tasks(&opts);
 }
 
+static char *get_maintpath(void)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *p = the_repository->worktree ?
+		the_repository->worktree : the_repository->gitdir;
+
+	strbuf_realpath(&sb, p, 1);
+	return strbuf_detach(&sb, NULL);
+}
+
 static int maintenance_register(void)
 {
+	int rc;
 	char *config_value;
 	struct child_process config_set = CHILD_PROCESS_INIT;
 	struct child_process config_get = CHILD_PROCESS_INIT;
+	char *maintpath = get_maintpath();
 
 	/* Disable foreground maintenance */
 	git_config_set("maintenance.auto", "false");
@@ -1457,74 +1490,408 @@
 
 	config_get.git_cmd = 1;
 	strvec_pushl(&config_get.args, "config", "--global", "--get",
-		     "--fixed-value", "maintenance.repo",
-		     the_repository->worktree ? the_repository->worktree
-					      : the_repository->gitdir,
-			 NULL);
+		     "--fixed-value", "maintenance.repo", maintpath, NULL);
 	config_get.out = -1;
 
-	if (start_command(&config_get))
-		return error(_("failed to run 'git config'"));
+	if (start_command(&config_get)) {
+		rc = error(_("failed to run 'git config'"));
+		goto done;
+	}
 
 	/* We already have this value in our config! */
-	if (!finish_command(&config_get))
-		return 0;
+	if (!finish_command(&config_get)) {
+		rc = 0;
+		goto done;
+	}
 
 	config_set.git_cmd = 1;
 	strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
-		     the_repository->worktree ? the_repository->worktree
-					      : the_repository->gitdir,
-		     NULL);
+		     maintpath, NULL);
 
-	return run_command(&config_set);
+	rc = run_command(&config_set);
+
+done:
+	free(maintpath);
+	return rc;
 }
 
 static int maintenance_unregister(void)
 {
+	int rc;
 	struct child_process config_unset = CHILD_PROCESS_INIT;
+	char *maintpath = get_maintpath();
 
 	config_unset.git_cmd = 1;
 	strvec_pushl(&config_unset.args, "config", "--global", "--unset",
-		     "--fixed-value", "maintenance.repo",
-		     the_repository->worktree ? the_repository->worktree
-					      : the_repository->gitdir,
-		     NULL);
+		     "--fixed-value", "maintenance.repo", maintpath, NULL);
 
-	return run_command(&config_unset);
+	rc = run_command(&config_unset);
+	free(maintpath);
+	return rc;
+}
+
+static const char *get_frequency(enum schedule_priority schedule)
+{
+	switch (schedule) {
+	case SCHEDULE_HOURLY:
+		return "hourly";
+	case SCHEDULE_DAILY:
+		return "daily";
+	case SCHEDULE_WEEKLY:
+		return "weekly";
+	default:
+		BUG("invalid schedule %d", schedule);
+	}
+}
+
+static char *launchctl_service_name(const char *frequency)
+{
+	struct strbuf label = STRBUF_INIT;
+	strbuf_addf(&label, "org.git-scm.git.%s", frequency);
+	return strbuf_detach(&label, NULL);
+}
+
+static char *launchctl_service_filename(const char *name)
+{
+	char *expanded;
+	struct strbuf filename = STRBUF_INIT;
+	strbuf_addf(&filename, "~/Library/LaunchAgents/%s.plist", name);
+
+	expanded = expand_user_path(filename.buf, 1);
+	if (!expanded)
+		die(_("failed to expand path '%s'"), filename.buf);
+
+	strbuf_release(&filename);
+	return expanded;
+}
+
+static char *launchctl_get_uid(void)
+{
+	return xstrfmt("gui/%d", getuid());
+}
+
+static int launchctl_boot_plist(int enable, const char *filename, const char *cmd)
+{
+	int result;
+	struct child_process child = CHILD_PROCESS_INIT;
+	char *uid = launchctl_get_uid();
+
+	strvec_split(&child.args, cmd);
+	if (enable)
+		strvec_push(&child.args, "bootstrap");
+	else
+		strvec_push(&child.args, "bootout");
+	strvec_push(&child.args, uid);
+	strvec_push(&child.args, filename);
+
+	child.no_stderr = 1;
+	child.no_stdout = 1;
+
+	if (start_command(&child))
+		die(_("failed to start launchctl"));
+
+	result = finish_command(&child);
+
+	free(uid);
+	return result;
+}
+
+static int launchctl_remove_plist(enum schedule_priority schedule, const char *cmd)
+{
+	const char *frequency = get_frequency(schedule);
+	char *name = launchctl_service_name(frequency);
+	char *filename = launchctl_service_filename(name);
+	int result = launchctl_boot_plist(0, filename, cmd);
+	unlink(filename);
+	free(filename);
+	free(name);
+	return result;
+}
+
+static int launchctl_remove_plists(const char *cmd)
+{
+	return launchctl_remove_plist(SCHEDULE_HOURLY, cmd) ||
+		launchctl_remove_plist(SCHEDULE_DAILY, cmd) ||
+		launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
+}
+
+static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+{
+	FILE *plist;
+	int i;
+	const char *preamble, *repeat;
+	const char *frequency = get_frequency(schedule);
+	char *name = launchctl_service_name(frequency);
+	char *filename = launchctl_service_filename(name);
+
+	if (safe_create_leading_directories(filename))
+		die(_("failed to create directories for '%s'"), filename);
+	plist = xfopen(filename, "w");
+
+	preamble = "<?xml version=\"1.0\"?>\n"
+		   "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+		   "<plist version=\"1.0\">"
+		   "<dict>\n"
+		   "<key>Label</key><string>%s</string>\n"
+		   "<key>ProgramArguments</key>\n"
+		   "<array>\n"
+		   "<string>%s/git</string>\n"
+		   "<string>--exec-path=%s</string>\n"
+		   "<string>for-each-repo</string>\n"
+		   "<string>--config=maintenance.repo</string>\n"
+		   "<string>maintenance</string>\n"
+		   "<string>run</string>\n"
+		   "<string>--schedule=%s</string>\n"
+		   "</array>\n"
+		   "<key>StartCalendarInterval</key>\n"
+		   "<array>\n";
+	fprintf(plist, preamble, name, exec_path, exec_path, frequency);
+
+	switch (schedule) {
+	case SCHEDULE_HOURLY:
+		repeat = "<dict>\n"
+			 "<key>Hour</key><integer>%d</integer>\n"
+			 "<key>Minute</key><integer>0</integer>\n"
+			 "</dict>\n";
+		for (i = 1; i <= 23; i++)
+			fprintf(plist, repeat, i);
+		break;
+
+	case SCHEDULE_DAILY:
+		repeat = "<dict>\n"
+			 "<key>Day</key><integer>%d</integer>\n"
+			 "<key>Hour</key><integer>0</integer>\n"
+			 "<key>Minute</key><integer>0</integer>\n"
+			 "</dict>\n";
+		for (i = 1; i <= 6; i++)
+			fprintf(plist, repeat, i);
+		break;
+
+	case SCHEDULE_WEEKLY:
+		fprintf(plist,
+			"<dict>\n"
+			"<key>Day</key><integer>0</integer>\n"
+			"<key>Hour</key><integer>0</integer>\n"
+			"<key>Minute</key><integer>0</integer>\n"
+			"</dict>\n");
+		break;
+
+	default:
+		/* unreachable */
+		break;
+	}
+	fprintf(plist, "</array>\n</dict>\n</plist>\n");
+	fclose(plist);
+
+	/* bootout might fail if not already running, so ignore */
+	launchctl_boot_plist(0, filename, cmd);
+	if (launchctl_boot_plist(1, filename, cmd))
+		die(_("failed to bootstrap service %s"), filename);
+
+	free(filename);
+	free(name);
+	return 0;
+}
+
+static int launchctl_add_plists(const char *cmd)
+{
+	const char *exec_path = git_exec_path();
+
+	return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY, cmd) ||
+		launchctl_schedule_plist(exec_path, SCHEDULE_DAILY, cmd) ||
+		launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY, cmd);
+}
+
+static int launchctl_update_schedule(int run_maintenance, int fd, const char *cmd)
+{
+	if (run_maintenance)
+		return launchctl_add_plists(cmd);
+	else
+		return launchctl_remove_plists(cmd);
+}
+
+static char *schtasks_task_name(const char *frequency)
+{
+	struct strbuf label = STRBUF_INIT;
+	strbuf_addf(&label, "Git Maintenance (%s)", frequency);
+	return strbuf_detach(&label, NULL);
+}
+
+static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd)
+{
+	int result;
+	struct strvec args = STRVEC_INIT;
+	const char *frequency = get_frequency(schedule);
+	char *name = schtasks_task_name(frequency);
+
+	strvec_split(&args, cmd);
+	strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
+
+	result = run_command_v_opt(args.v, 0);
+
+	strvec_clear(&args);
+	free(name);
+	return result;
+}
+
+static int schtasks_remove_tasks(const char *cmd)
+{
+	return schtasks_remove_task(SCHEDULE_HOURLY, cmd) ||
+		schtasks_remove_task(SCHEDULE_DAILY, cmd) ||
+		schtasks_remove_task(SCHEDULE_WEEKLY, cmd);
+}
+
+static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+{
+	int result;
+	struct child_process child = CHILD_PROCESS_INIT;
+	const char *xml;
+	struct tempfile *tfile;
+	const char *frequency = get_frequency(schedule);
+	char *name = schtasks_task_name(frequency);
+	struct strbuf tfilename = STRBUF_INIT;
+
+	strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX",
+		    get_git_common_dir(), frequency);
+	tfile = xmks_tempfile(tfilename.buf);
+	strbuf_release(&tfilename);
+
+	if (!fdopen_tempfile(tfile, "w"))
+		die(_("failed to create temp xml file"));
+
+	xml = "<?xml version=\"1.0\" ?>\n"
+	      "<Task version=\"1.4\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n"
+	      "<Triggers>\n"
+	      "<CalendarTrigger>\n";
+	fputs(xml, tfile->fp);
+
+	switch (schedule) {
+	case SCHEDULE_HOURLY:
+		fprintf(tfile->fp,
+			"<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n"
+			"<Enabled>true</Enabled>\n"
+			"<ScheduleByDay>\n"
+			"<DaysInterval>1</DaysInterval>\n"
+			"</ScheduleByDay>\n"
+			"<Repetition>\n"
+			"<Interval>PT1H</Interval>\n"
+			"<Duration>PT23H</Duration>\n"
+			"<StopAtDurationEnd>false</StopAtDurationEnd>\n"
+			"</Repetition>\n");
+		break;
+
+	case SCHEDULE_DAILY:
+		fprintf(tfile->fp,
+			"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+			"<Enabled>true</Enabled>\n"
+			"<ScheduleByWeek>\n"
+			"<DaysOfWeek>\n"
+			"<Monday />\n"
+			"<Tuesday />\n"
+			"<Wednesday />\n"
+			"<Thursday />\n"
+			"<Friday />\n"
+			"<Saturday />\n"
+			"</DaysOfWeek>\n"
+			"<WeeksInterval>1</WeeksInterval>\n"
+			"</ScheduleByWeek>\n");
+		break;
+
+	case SCHEDULE_WEEKLY:
+		fprintf(tfile->fp,
+			"<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n"
+			"<Enabled>true</Enabled>\n"
+			"<ScheduleByWeek>\n"
+			"<DaysOfWeek>\n"
+			"<Sunday />\n"
+			"</DaysOfWeek>\n"
+			"<WeeksInterval>1</WeeksInterval>\n"
+			"</ScheduleByWeek>\n");
+		break;
+
+	default:
+		break;
+	}
+
+	xml = "</CalendarTrigger>\n"
+	      "</Triggers>\n"
+	      "<Principals>\n"
+	      "<Principal id=\"Author\">\n"
+	      "<LogonType>InteractiveToken</LogonType>\n"
+	      "<RunLevel>LeastPrivilege</RunLevel>\n"
+	      "</Principal>\n"
+	      "</Principals>\n"
+	      "<Settings>\n"
+	      "<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n"
+	      "<Enabled>true</Enabled>\n"
+	      "<Hidden>true</Hidden>\n"
+	      "<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>\n"
+	      "<WakeToRun>false</WakeToRun>\n"
+	      "<ExecutionTimeLimit>PT72H</ExecutionTimeLimit>\n"
+	      "<Priority>7</Priority>\n"
+	      "</Settings>\n"
+	      "<Actions Context=\"Author\">\n"
+	      "<Exec>\n"
+	      "<Command>\"%s\\git.exe\"</Command>\n"
+	      "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
+	      "</Exec>\n"
+	      "</Actions>\n"
+	      "</Task>\n";
+	fprintf(tfile->fp, xml, exec_path, exec_path, frequency);
+	strvec_split(&child.args, cmd);
+	strvec_pushl(&child.args, "/create", "/tn", name, "/f", "/xml",
+				  get_tempfile_path(tfile), NULL);
+	close_tempfile_gently(tfile);
+
+	child.no_stdout = 1;
+	child.no_stderr = 1;
+
+	if (start_command(&child))
+		die(_("failed to start schtasks"));
+	result = finish_command(&child);
+
+	delete_tempfile(&tfile);
+	free(name);
+	return result;
+}
+
+static int schtasks_schedule_tasks(const char *cmd)
+{
+	const char *exec_path = git_exec_path();
+
+	return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY, cmd) ||
+		schtasks_schedule_task(exec_path, SCHEDULE_DAILY, cmd) ||
+		schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY, cmd);
+}
+
+static int schtasks_update_schedule(int run_maintenance, int fd, const char *cmd)
+{
+	if (run_maintenance)
+		return schtasks_schedule_tasks(cmd);
+	else
+		return schtasks_remove_tasks(cmd);
 }
 
 #define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
 #define END_LINE "# END GIT MAINTENANCE SCHEDULE"
 
-static int update_background_schedule(int run_maintenance)
+static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
 {
 	int result = 0;
 	int in_old_region = 0;
 	struct child_process crontab_list = CHILD_PROCESS_INIT;
 	struct child_process crontab_edit = CHILD_PROCESS_INIT;
 	FILE *cron_list, *cron_in;
-	const char *crontab_name;
 	struct strbuf line = STRBUF_INIT;
-	struct lock_file lk;
-	char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
 
-	if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
-		return error(_("another process is scheduling background maintenance"));
-
-	crontab_name = getenv("GIT_TEST_CRONTAB");
-	if (!crontab_name)
-		crontab_name = "crontab";
-
-	strvec_split(&crontab_list.args, crontab_name);
+	strvec_split(&crontab_list.args, cmd);
 	strvec_push(&crontab_list.args, "-l");
 	crontab_list.in = -1;
-	crontab_list.out = dup(get_lock_file_fd(&lk));
+	crontab_list.out = dup(fd);
 	crontab_list.git_cmd = 0;
 
-	if (start_command(&crontab_list)) {
-		result = error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
-		goto cleanup;
-	}
+	if (start_command(&crontab_list))
+		return error(_("failed to run 'crontab -l'; your system might not support 'cron'"));
 
 	/* Ignore exit code, as an empty crontab will return error. */
 	finish_command(&crontab_list);
@@ -1533,17 +1900,15 @@
 	 * Read from the .lock file, filtering out the old
 	 * schedule while appending the new schedule.
 	 */
-	cron_list = fdopen(get_lock_file_fd(&lk), "r");
+	cron_list = fdopen(fd, "r");
 	rewind(cron_list);
 
-	strvec_split(&crontab_edit.args, crontab_name);
+	strvec_split(&crontab_edit.args, cmd);
 	crontab_edit.in = -1;
 	crontab_edit.git_cmd = 0;
 
-	if (start_command(&crontab_edit)) {
-		result = error(_("failed to run 'crontab'; your system might not support 'cron'"));
-		goto cleanup;
-	}
+	if (start_command(&crontab_edit))
+		return error(_("failed to run 'crontab'; your system might not support 'cron'"));
 
 	cron_in = fdopen(crontab_edit.in, "w");
 	if (!cron_in) {
@@ -1587,14 +1952,54 @@
 	close(crontab_edit.in);
 
 done_editing:
-	if (finish_command(&crontab_edit)) {
+	if (finish_command(&crontab_edit))
 		result = error(_("'crontab' died"));
-		goto cleanup;
-	}
-	fclose(cron_list);
+	else
+		fclose(cron_list);
+	return result;
+}
 
-cleanup:
+#if defined(__APPLE__)
+static const char platform_scheduler[] = "launchctl";
+#elif defined(GIT_WINDOWS_NATIVE)
+static const char platform_scheduler[] = "schtasks";
+#else
+static const char platform_scheduler[] = "crontab";
+#endif
+
+static int update_background_schedule(int enable)
+{
+	int result;
+	const char *scheduler = platform_scheduler;
+	const char *cmd = scheduler;
+	char *testing;
+	struct lock_file lk;
+	char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
+
+	testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
+	if (testing) {
+		char *sep = strchr(testing, ':');
+		if (!sep)
+			die("GIT_TEST_MAINT_SCHEDULER unparseable: %s", testing);
+		*sep = '\0';
+		scheduler = testing;
+		cmd = sep + 1;
+	}
+
+	if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0)
+		return error(_("another process is scheduling background maintenance"));
+
+	if (!strcmp(scheduler, "launchctl"))
+		result = launchctl_update_schedule(enable, get_lock_file_fd(&lk), cmd);
+	else if (!strcmp(scheduler, "schtasks"))
+		result = schtasks_update_schedule(enable, get_lock_file_fd(&lk), cmd);
+	else if (!strcmp(scheduler, "crontab"))
+		result = crontab_update_schedule(enable, get_lock_file_fd(&lk), cmd);
+	else
+		die("unknown background scheduler: %s", scheduler);
+
 	rollback_lock_file(&lk);
+	free(testing);
 	return result;
 }
 
diff --git a/builtin/grep.c b/builtin/grep.c
index ca259af..4e91a25 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -216,8 +216,6 @@
 		int err;
 		struct grep_opt *o = grep_opt_dup(opt);
 		o->output = strbuf_out;
-		if (i)
-			o->debug = 0;
 		compile_grep_patterns(o);
 		err = pthread_create(&threads[i], NULL, run, o);
 
@@ -508,6 +506,10 @@
 
 	for (nr = 0; nr < repo->index->cache_nr; nr++) {
 		const struct cache_entry *ce = repo->index->cache[nr];
+
+		if (!cached && ce_skip_worktree(ce))
+			continue;
+
 		strbuf_setlen(&name, name_base_len);
 		strbuf_addstr(&name, ce->name);
 
@@ -520,8 +522,7 @@
 			 * cache entry are identical, even if worktree file has
 			 * been modified, so use cache version instead
 			 */
-			if (cached || (ce->ce_flags & CE_VALID) ||
-			    ce_skip_worktree(ce)) {
+			if (cached || (ce->ce_flags & CE_VALID)) {
 				if (ce_stage(ce) || ce_intent_to_add(ce))
 					continue;
 				hit |= grep_oid(opt, &ce->oid, name.buf,
@@ -936,9 +937,6 @@
 			   N_("indicate hit with exit status without output")),
 		OPT_BOOL(0, "all-match", &opt.all_match,
 			N_("show only matches from files that match all patterns")),
-		OPT_SET_INT_F(0, "debug", &opt.debug,
-			      N_("show parse tree for grep expression"),
-			      1, PARSE_OPT_HIDDEN),
 		OPT_GROUP(""),
 		{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
 			N_("pager"), N_("show matching files in the pager"),
@@ -1157,6 +1155,9 @@
 	if (!use_index && (untracked || cached))
 		die(_("--cached or --untracked cannot be used with --no-index"));
 
+	if (untracked && cached)
+		die(_("--untracked cannot be used with --cached"));
+
 	if (!use_index || untracked) {
 		int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
 		hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 4b8d86e..bad5748 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -17,7 +17,7 @@
 #include "promisor-remote.h"
 
 static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
 
 struct object_entry {
 	struct pack_idx_entry idx;
@@ -1436,15 +1436,15 @@
 	free(sorted_by_pos);
 }
 
-static const char *derive_filename(const char *pack_name, const char *suffix,
-				   struct strbuf *buf)
+static const char *derive_filename(const char *pack_name, const char *strip,
+				   const char *suffix, struct strbuf *buf)
 {
 	size_t len;
-	if (!strip_suffix(pack_name, ".pack", &len))
-		die(_("packfile name '%s' does not end with '.pack'"),
-		    pack_name);
+	if (!strip_suffix(pack_name, strip, &len) || !len ||
+	    pack_name[len - 1] != '.')
+		die(_("packfile name '%s' does not end with '.%s'"),
+		    pack_name, strip);
 	strbuf_add(buf, pack_name, len);
-	strbuf_addch(buf, '.');
 	strbuf_addstr(buf, suffix);
 	return buf->buf;
 }
@@ -1459,7 +1459,7 @@
 	int msg_len = strlen(msg);
 
 	if (pack_name)
-		filename = derive_filename(pack_name, suffix, &name_buf);
+		filename = derive_filename(pack_name, "pack", suffix, &name_buf);
 	else
 		filename = odb_pack_name(&name_buf, hash, suffix);
 
@@ -1484,12 +1484,14 @@
 
 static void final(const char *final_pack_name, const char *curr_pack_name,
 		  const char *final_index_name, const char *curr_index_name,
+		  const char *final_rev_index_name, const char *curr_rev_index_name,
 		  const char *keep_msg, const char *promisor_msg,
 		  unsigned char *hash)
 {
 	const char *report = "pack";
 	struct strbuf pack_name = STRBUF_INIT;
 	struct strbuf index_name = STRBUF_INIT;
+	struct strbuf rev_index_name = STRBUF_INIT;
 	int err;
 
 	if (!from_stdin) {
@@ -1524,6 +1526,16 @@
 	} else
 		chmod(final_index_name, 0444);
 
+	if (curr_rev_index_name) {
+		if (final_rev_index_name != curr_rev_index_name) {
+			if (!final_rev_index_name)
+				final_rev_index_name = odb_pack_name(&rev_index_name, hash, "rev");
+			if (finalize_object_file(curr_rev_index_name, final_rev_index_name))
+				die(_("cannot store reverse index file"));
+		} else
+			chmod(final_rev_index_name, 0444);
+	}
+
 	if (do_fsck_object) {
 		struct packed_git *p;
 		p = add_packed_git(final_index_name, strlen(final_index_name), 0);
@@ -1553,6 +1565,7 @@
 		}
 	}
 
+	strbuf_release(&rev_index_name);
 	strbuf_release(&index_name);
 	strbuf_release(&pack_name);
 }
@@ -1578,6 +1591,12 @@
 		}
 		return 0;
 	}
+	if (!strcmp(k, "pack.writereverseindex")) {
+		if (git_config_bool(k, v))
+			opts->flags |= WRITE_REV;
+		else
+			opts->flags &= ~WRITE_REV;
+	}
 	return git_default_config(k, v, cb);
 }
 
@@ -1641,7 +1660,7 @@
 	/*
 	 * Get rid of the idx file as we do not need it anymore.
 	 * NEEDSWORK: extract this bit from free_pack_by_name() in
-	 * sha1-file.c, perhaps?  It shouldn't matter very much as we
+	 * object-file.c, perhaps?  It shouldn't matter very much as we
 	 * know we haven't installed this pack (hence we never have
 	 * read anything from it).
 	 */
@@ -1693,14 +1712,32 @@
 	}
 }
 
+static int print_dangling_gitmodules(struct fsck_options *o,
+				     const struct object_id *oid,
+				     enum object_type object_type,
+				     int msg_type, const char *message)
+{
+	/*
+	 * NEEDSWORK: Plumb the MSG_ID (from fsck.c) here and use it
+	 * instead of relying on this string check.
+	 */
+	if (starts_with(message, "gitmodulesMissing")) {
+		printf("%s\n", oid_to_hex(oid));
+		return 0;
+	}
+	return fsck_error_function(o, oid, object_type, msg_type, message);
+}
+
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
 {
-	int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
+	int i, fix_thin_pack = 0, verify = 0, stat_only = 0, rev_index;
 	const char *curr_index;
-	const char *index_name = NULL, *pack_name = NULL;
+	const char *curr_rev_index = NULL;
+	const char *index_name = NULL, *pack_name = NULL, *rev_index_name = NULL;
 	const char *keep_msg = NULL;
 	const char *promisor_msg = NULL;
 	struct strbuf index_name_buf = STRBUF_INIT;
+	struct strbuf rev_index_name_buf = STRBUF_INIT;
 	struct pack_idx_entry **idx_objects;
 	struct pack_idx_option opts;
 	unsigned char pack_hash[GIT_MAX_RAWSZ];
@@ -1727,6 +1764,11 @@
 	if (prefix && chdir(prefix))
 		die(_("Cannot come back to cwd"));
 
+	if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
+		rev_index = 1;
+	else
+		rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
+
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
@@ -1805,6 +1847,10 @@
 				if (hash_algo == GIT_HASH_UNKNOWN)
 					die(_("unknown hash algorithm '%s'"), arg);
 				repo_set_hash_algo(the_repository, hash_algo);
+			} else if (!strcmp(arg, "--rev-index")) {
+				rev_index = 1;
+			} else if (!strcmp(arg, "--no-rev-index")) {
+				rev_index = 0;
 			} else
 				usage(index_pack_usage);
 			continue;
@@ -1824,7 +1870,16 @@
 	if (from_stdin && hash_algo)
 		die(_("--object-format cannot be used with --stdin"));
 	if (!index_name && pack_name)
-		index_name = derive_filename(pack_name, "idx", &index_name_buf);
+		index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
+
+	opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
+	if (rev_index) {
+		opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
+		if (index_name)
+			rev_index_name = derive_filename(index_name,
+							 "idx", "rev",
+							 &rev_index_name_buf);
+	}
 
 	if (verify) {
 		if (!index_name)
@@ -1878,25 +1933,38 @@
 	for (i = 0; i < nr_objects; i++)
 		idx_objects[i] = &objects[i].idx;
 	curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
+	if (rev_index)
+		curr_rev_index = write_rev_file(rev_index_name, idx_objects,
+						nr_objects, pack_hash,
+						opts.flags);
 	free(idx_objects);
 
 	if (!verify)
 		final(pack_name, curr_pack,
 		      index_name, curr_index,
+		      rev_index_name, curr_rev_index,
 		      keep_msg, promisor_msg,
 		      pack_hash);
 	else
 		close(input_fd);
 
-	if (do_fsck_object && fsck_finish(&fsck_options))
-		die(_("fsck error in pack objects"));
+	if (do_fsck_object) {
+		struct fsck_options fo = fsck_options;
+
+		fo.error_func = print_dangling_gitmodules;
+		if (fsck_finish(&fo))
+			die(_("fsck error in pack objects"));
+	}
 
 	free(objects);
 	strbuf_release(&index_name_buf);
+	strbuf_release(&rev_index_name_buf);
 	if (pack_name == NULL)
 		free((void *) curr_pack);
 	if (index_name == NULL)
 		free((void *) curr_index);
+	if (rev_index_name == NULL)
+		free((void *) curr_rev_index);
 
 	/*
 	 * Let the caller know this pack is not self contained
diff --git a/builtin/log.c b/builtin/log.c
index f23ccdb..f67b67d 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -12,6 +12,7 @@
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
@@ -230,7 +231,7 @@
 
 	if (mailmap) {
 		rev->mailmap = xcalloc(1, sizeof(struct string_list));
-		read_mailmap(rev->mailmap, NULL);
+		read_mailmap(rev->mailmap);
 	}
 
 	if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) {
@@ -306,10 +307,11 @@
 
 static void log_show_early(struct rev_info *revs, struct commit_list *list)
 {
-	int i = revs->early_output, close_file = revs->diffopt.close_file;
+	int i = revs->early_output;
 	int show_header = 1;
+	int no_free = revs->diffopt.no_free;
 
-	revs->diffopt.close_file = 0;
+	revs->diffopt.no_free = 0;
 	sort_in_topological_order(&list, revs->sort_order);
 	while (list && i) {
 		struct commit *commit = list->item;
@@ -326,8 +328,8 @@
 		case commit_ignore:
 			break;
 		case commit_error:
-			if (close_file)
-				fclose(revs->diffopt.file);
+			revs->diffopt.no_free = no_free;
+			diff_free(&revs->diffopt);
 			return;
 		}
 		list = list->next;
@@ -335,8 +337,8 @@
 
 	/* Did we already get enough commits for the early output? */
 	if (!i) {
-		if (close_file)
-			fclose(revs->diffopt.file);
+		revs->diffopt.no_free = 0;
+		diff_free(&revs->diffopt);
 		return;
 	}
 
@@ -400,7 +402,7 @@
 {
 	struct commit *commit;
 	int saved_nrl = 0;
-	int saved_dcctc = 0, close_file = rev->diffopt.close_file;
+	int saved_dcctc = 0;
 
 	if (rev->early_output)
 		setup_early_output();
@@ -416,7 +418,7 @@
 	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
 	 * retain that state information if replacing rev->diffopt in this loop
 	 */
-	rev->diffopt.close_file = 0;
+	rev->diffopt.no_free = 1;
 	while ((commit = get_revision(rev)) != NULL) {
 		if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
 			/*
@@ -441,8 +443,8 @@
 	}
 	rev->diffopt.degraded_cc_to_c = saved_dcctc;
 	rev->diffopt.needed_rename_limit = saved_nrl;
-	if (close_file)
-		fclose(rev->diffopt.file);
+	rev->diffopt.no_free = 0;
+	diff_free(&rev->diffopt);
 
 	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
 	    rev->diffopt.flags.check_failed) {
@@ -607,15 +609,10 @@
 static void show_setup_revisions_tweak(struct rev_info *rev,
 				       struct setup_revision_opt *opt)
 {
-	if (rev->ignore_merges < 0) {
-		/* There was no "-m" variant on the command line */
-		rev->ignore_merges = 0;
-		if (!rev->first_parent_only && !rev->combine_merges) {
-			/* No "--first-parent", "-c", or "--cc" */
-			rev->combine_merges = 1;
-			rev->dense_combined_merges = 1;
-		}
-	}
+	if (rev->first_parent_only)
+		diff_merges_default_to_first_parent(rev);
+	else
+		diff_merges_default_to_dense_combined(rev);
 	if (!rev->diffopt.output_format)
 		rev->diffopt.output_format = DIFF_FORMAT_PATCH;
 }
@@ -736,12 +733,8 @@
 	    rev->prune_data.nr == 1)
 		rev->diffopt.flags.follow_renames = 1;
 
-	/* Turn --cc/-c into -p --cc/-c when -p was not given */
-	if (!rev->diffopt.output_format && rev->combine_merges)
-		rev->diffopt.output_format = DIFF_FORMAT_PATCH;
-
-	if (rev->first_parent_only && rev->ignore_merges < 0)
-		rev->ignore_merges = 0;
+	if (rev->first_parent_only)
+		diff_merges_default_to_first_parent(rev);
 }
 
 int cmd_log(int argc, const char **argv, const char *prefix)
@@ -1231,14 +1224,20 @@
 		 */
 		struct diff_options opts;
 		struct strvec other_arg = STRVEC_INIT;
+		struct range_diff_options range_diff_opts = {
+			.creation_factor = rev->creation_factor,
+			.dual_color = 1,
+			.diffopt = &opts,
+			.other_arg = &other_arg
+		};
+
 		diff_setup(&opts);
 		opts.file = rev->diffopt.file;
 		opts.use_color = rev->diffopt.use_color;
 		diff_setup_done(&opts);
 		fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
 		get_notes_args(&other_arg, rev);
-		show_range_diff(rev->rdiff1, rev->rdiff2,
-				rev->creation_factor, 1, &opts, &other_arg);
+		show_range_diff(rev->rdiff1, rev->rdiff2, &range_diff_opts);
 		strvec_clear(&other_arg);
 	}
 }
@@ -1680,7 +1679,7 @@
 				    struct commit *head)
 {
 	const char *head_oid = oid_to_hex(&head->object.oid);
-	int prev_is_range = !!strstr(prev, "..");
+	int prev_is_range = is_range_diff_range(prev);
 
 	if (prev_is_range)
 		strbuf_addstr(r1, prev);
@@ -1963,7 +1962,7 @@
 		 * file, but but we must instruct it not to close after each
 		 * diff.
 		 */
-		rev.diffopt.close_file = 0;
+		rev.diffopt.no_free = 1;
 	} else {
 		int saved;
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index c8eae89..f6f9e48 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -35,6 +35,7 @@
 static int debug_mode;
 static int show_eol;
 static int recurse_submodules;
+static int skipping_duplicates;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -312,45 +313,59 @@
 		if (show_killed)
 			show_killed_files(repo->index, dir);
 	}
-	if (show_cached || show_stage) {
-		for (i = 0; i < repo->index->cache_nr; i++) {
-			const struct cache_entry *ce = repo->index->cache[i];
 
-			construct_fullname(&fullname, repo, ce);
+	if (!(show_cached || show_stage || show_deleted || show_modified))
+		return;
+	for (i = 0; i < repo->index->cache_nr; i++) {
+		const struct cache_entry *ce = repo->index->cache[i];
+		struct stat st;
+		int stat_err;
 
-			if ((dir->flags & DIR_SHOW_IGNORED) &&
-			    !ce_excluded(dir, repo->index, fullname.buf, ce))
-				continue;
-			if (show_unmerged && !ce_stage(ce))
-				continue;
-			if (ce->ce_flags & CE_UPDATE)
-				continue;
+		construct_fullname(&fullname, repo, ce);
+
+		if ((dir->flags & DIR_SHOW_IGNORED) &&
+			!ce_excluded(dir, repo->index, fullname.buf, ce))
+			continue;
+		if (ce->ce_flags & CE_UPDATE)
+			continue;
+		if ((show_cached || show_stage) &&
+		    (!show_unmerged || ce_stage(ce))) {
 			show_ce(repo, dir, ce, fullname.buf,
 				ce_stage(ce) ? tag_unmerged :
 				(ce_skip_worktree(ce) ? tag_skip_worktree :
 				 tag_cached));
+			if (skipping_duplicates)
+				goto skip_to_next_name;
 		}
-	}
-	if (show_deleted || show_modified) {
-		for (i = 0; i < repo->index->cache_nr; i++) {
-			const struct cache_entry *ce = repo->index->cache[i];
-			struct stat st;
-			int err;
 
-			construct_fullname(&fullname, repo, ce);
+		if (!(show_deleted || show_modified))
+			continue;
+		if (ce_skip_worktree(ce))
+			continue;
+		stat_err = lstat(fullname.buf, &st);
+		if (stat_err && (errno != ENOENT && errno != ENOTDIR))
+			error_errno("cannot lstat '%s'", fullname.buf);
+		if (stat_err && show_deleted) {
+			show_ce(repo, dir, ce, fullname.buf, tag_removed);
+			if (skipping_duplicates)
+				goto skip_to_next_name;
+		}
+		if (show_modified &&
+		    (stat_err || ie_modified(repo->index, ce, &st, 0))) {
+			show_ce(repo, dir, ce, fullname.buf, tag_modified);
+			if (skipping_duplicates)
+				goto skip_to_next_name;
+		}
+		continue;
 
-			if ((dir->flags & DIR_SHOW_IGNORED) &&
-			    !ce_excluded(dir, repo->index, fullname.buf, ce))
-				continue;
-			if (ce->ce_flags & CE_UPDATE)
-				continue;
-			if (ce_skip_worktree(ce))
-				continue;
-			err = lstat(fullname.buf, &st);
-			if (show_deleted && err)
-				show_ce(repo, dir, ce, fullname.buf, tag_removed);
-			if (show_modified && ie_modified(repo->index, ce, &st, 0))
-				show_ce(repo, dir, ce, fullname.buf, tag_modified);
+skip_to_next_name:
+		{
+			int j;
+			struct cache_entry **cache = repo->index->cache;
+			for (j = i + 1; j < repo->index->cache_nr; j++)
+				if (strcmp(ce->name, cache[j]->name))
+					break;
+			i = j - 1; /* compensate for the for loop */
 		}
 	}
 
@@ -578,6 +593,8 @@
 			N_("pretend that paths removed since <tree-ish> are still present")),
 		OPT__ABBREV(&abbrev),
 		OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
+		OPT_BOOL(0, "deduplicate", &skipping_duplicates,
+			 N_("suppress duplicate entries")),
 		OPT_END()
 	};
 
@@ -617,6 +634,8 @@
 		 * you also show the stage information.
 		 */
 		show_stage = 1;
+	if (show_tag || show_stage)
+		skipping_duplicates = 0;
 	if (dir.exclude_per_dir)
 		exc_given = 1;
 
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 092917e..ef60475 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -45,7 +45,8 @@
 	int show_symref_target = 0;
 	const char *uploadpack = NULL;
 	const char **pattern = NULL;
-	struct strvec ref_prefixes = STRVEC_INIT;
+	struct transport_ls_refs_options transport_options =
+		TRANSPORT_LS_REFS_OPTIONS_INIT;
 	int i;
 	struct string_list server_options = STRING_LIST_INIT_DUP;
 
@@ -94,9 +95,9 @@
 	}
 
 	if (flags & REF_TAGS)
-		strvec_push(&ref_prefixes, "refs/tags/");
+		strvec_push(&transport_options.ref_prefixes, "refs/tags/");
 	if (flags & REF_HEADS)
-		strvec_push(&ref_prefixes, "refs/heads/");
+		strvec_push(&transport_options.ref_prefixes, "refs/heads/");
 
 	remote = remote_get(dest);
 	if (!remote) {
@@ -118,7 +119,7 @@
 	if (server_options.nr)
 		transport->server_options = &server_options;
 
-	ref = transport_get_remote_refs(transport, &ref_prefixes);
+	ref = transport_get_remote_refs(transport, &transport_options);
 	if (ref) {
 		int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
 		repo_set_hash_algo(the_repository, hash_algo);
diff --git a/builtin/merge.c b/builtin/merge.c
index 1cff730..eb00b27 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -14,6 +14,7 @@
 #include "lockfile.h"
 #include "run-command.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "refs.h"
 #include "refspec.h"
 #include "commit.h"
@@ -409,7 +410,7 @@
 	printf(_("Squash commit -- not updating HEAD\n"));
 
 	repo_init_revisions(the_repository, &rev, NULL);
-	rev.ignore_merges = 1;
+	diff_merges_suppress(&rev);
 	rev.commit_format = CMIT_FMT_MEDIUM;
 
 	commit->object.flags |= UNINTERESTING;
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 4982d3a..41a399a 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,179 +1,110 @@
 #include "builtin.h"
+#include "parse-options.h"
 #include "tag.h"
 #include "replace-object.h"
 #include "object-store.h"
+#include "fsck.h"
+#include "config.h"
 
-/*
- * A signature file has a very simple fixed format: four lines
- * of "object <sha1>" + "type <typename>" + "tag <tagname>" +
- * "tagger <committer>", followed by a blank line, a free-form tag
- * message and a signature block that git itself doesn't care about,
- * but that can be verified with gpg or similar.
- *
- * The first four lines are guaranteed to be at least 83 bytes:
- * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
- * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
- * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
- * the shortest possible tagger-line.
- */
+static char const * const builtin_mktag_usage[] = {
+	N_("git mktag"),
+	NULL
+};
+static int option_strict = 1;
 
-/*
- * We refuse to tag something we can't verify. Just because.
- */
-static int verify_object(const struct object_id *oid, const char *expected_type)
+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+
+static int mktag_config(const char *var, const char *value, void *cb)
 {
-	int ret = -1;
-	enum object_type type;
-	unsigned long size;
-	void *buffer = read_object_file(oid, &type, &size);
-	const struct object_id *repl = lookup_replace_object(the_repository, oid);
-
-	if (buffer) {
-		if (type == type_from_string(expected_type)) {
-			ret = check_object_signature(the_repository, repl,
-						     buffer, size,
-						     expected_type);
-		}
-		free(buffer);
-	}
-	return ret;
+	return fsck_config_internal(var, value, cb, &fsck_options);
 }
 
-static int verify_tag(char *buffer, unsigned long size)
+static int mktag_fsck_error_func(struct fsck_options *o,
+				 const struct object_id *oid,
+				 enum object_type object_type,
+				 int msg_type, const char *message)
 {
-	int typelen;
-	char type[20];
-	struct object_id oid;
-	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
-	size_t len;
+	switch (msg_type) {
+	case FSCK_WARN:
+		if (!option_strict) {
+			fprintf_ln(stderr, _("warning: tag input does not pass fsck: %s"), message);
+			return 0;
 
-	if (size < 84)
-		return error("wanna fool me ? you obviously got the size wrong !");
-
-	buffer[size] = 0;
-
-	/* Verify object line */
-	object = buffer;
-	if (memcmp(object, "object ", 7))
-		return error("char%d: does not start with \"object \"", 0);
-
-	if (parse_oid_hex(object + 7, &oid, &p))
-		return error("char%d: could not get SHA1 hash", 7);
-
-	/* Verify type line */
-	type_line = p + 1;
-	if (memcmp(type_line - 1, "\ntype ", 6))
-		return error("char%d: could not find \"\\ntype \"", 47);
-
-	/* Verify tag-line */
-	tag_line = strchr(type_line, '\n');
-	if (!tag_line)
-		return error("char%"PRIuMAX": could not find next \"\\n\"",
-				(uintmax_t) (type_line - buffer));
-	tag_line++;
-	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
-		return error("char%"PRIuMAX": no \"tag \" found",
-				(uintmax_t) (tag_line - buffer));
-
-	/* Get the actual type */
-	typelen = tag_line - type_line - strlen("type \n");
-	if (typelen >= sizeof(type))
-		return error("char%"PRIuMAX": type too long",
-				(uintmax_t) (type_line+5 - buffer));
-
-	memcpy(type, type_line+5, typelen);
-	type[typelen] = 0;
-
-	/* Verify that the object matches */
-	if (verify_object(&oid, type))
-		return error("char%d: could not verify object %s", 7, oid_to_hex(&oid));
-
-	/* Verify the tag-name: we don't allow control characters or spaces in it */
-	tag_line += 4;
-	for (;;) {
-		unsigned char c = *tag_line++;
-		if (c == '\n')
-			break;
-		if (c > ' ')
-			continue;
-		return error("char%"PRIuMAX": could not verify tag name",
-				(uintmax_t) (tag_line - buffer));
+		}
+		/* fallthrough */
+	case FSCK_ERROR:
+		/*
+		 * We treat both warnings and errors as errors, things
+		 * like missing "tagger" lines are "only" warnings
+		 * under fsck, we've always considered them an error.
+		 */
+		fprintf_ln(stderr, _("error: tag input does not pass fsck: %s"), message);
+		return 1;
+	default:
+		BUG(_("%d (FSCK_IGNORE?) should never trigger this callback"),
+		    msg_type);
 	}
+}
 
-	/* Verify the tagger line */
-	tagger_line = tag_line;
+static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
+{
+	int ret;
+	enum object_type type;
+	unsigned long size;
+	void *buffer;
+	const struct object_id *repl;
 
-	if (memcmp(tagger_line, "tagger ", 7))
-		return error("char%"PRIuMAX": could not find \"tagger \"",
-			(uintmax_t) (tagger_line - buffer));
+	buffer = read_object_file(tagged_oid, &type, &size);
+	if (!buffer)
+		die(_("could not read tagged object '%s'"),
+		    oid_to_hex(tagged_oid));
+	if (type != *tagged_type)
+		die(_("object '%s' tagged as '%s', but is a '%s' type"),
+		    oid_to_hex(tagged_oid),
+		    type_name(*tagged_type), type_name(type));
 
-	/*
-	 * Check for correct form for name and email
-	 * i.e. " <" followed by "> " on _this_ line
-	 * No angle brackets within the name or email address fields.
-	 * No spaces within the email address field.
-	 */
-	tagger_line += 7;
-	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
-		strpbrk(tagger_line, "<>\n") != lb+1 ||
-		strpbrk(lb+2, "><\n ") != rb)
-		return error("char%"PRIuMAX": malformed tagger field",
-			(uintmax_t) (tagger_line - buffer));
+	repl = lookup_replace_object(the_repository, tagged_oid);
+	ret = check_object_signature(the_repository, repl,
+				     buffer, size, type_name(*tagged_type));
+	free(buffer);
 
-	/* Check for author name, at least one character, space is acceptable */
-	if (lb == tagger_line)
-		return error("char%"PRIuMAX": missing tagger name",
-			(uintmax_t) (tagger_line - buffer));
-
-	/* timestamp, 1 or more digits followed by space */
-	tagger_line = rb + 2;
-	if (!(len = strspn(tagger_line, "0123456789")))
-		return error("char%"PRIuMAX": missing tag timestamp",
-			(uintmax_t) (tagger_line - buffer));
-	tagger_line += len;
-	if (*tagger_line != ' ')
-		return error("char%"PRIuMAX": malformed tag timestamp",
-			(uintmax_t) (tagger_line - buffer));
-	tagger_line++;
-
-	/* timezone, 5 digits [+-]hhmm, max. 1400 */
-	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
-	      strspn(tagger_line+1, "0123456789") == 4 &&
-	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
-		return error("char%"PRIuMAX": malformed tag timezone",
-			(uintmax_t) (tagger_line - buffer));
-	tagger_line += 6;
-
-	/* Verify the blank line separating the header from the body */
-	if (*tagger_line != '\n')
-		return error("char%"PRIuMAX": trailing garbage in tag header",
-			(uintmax_t) (tagger_line - buffer));
-
-	/* The actual stuff afterwards we don't care about.. */
-	return 0;
+	return ret;
 }
 
 int cmd_mktag(int argc, const char **argv, const char *prefix)
 {
+	static struct option builtin_mktag_options[] = {
+		OPT_BOOL(0, "strict", &option_strict,
+			 N_("enable more strict checking")),
+		OPT_END(),
+	};
 	struct strbuf buf = STRBUF_INIT;
+	struct object_id tagged_oid;
+	int tagged_type;
 	struct object_id result;
 
-	if (argc != 1)
-		usage("git mktag");
+	argc = parse_options(argc, argv, NULL,
+			     builtin_mktag_options,
+			     builtin_mktag_usage, 0);
 
-	if (strbuf_read(&buf, 0, 4096) < 0) {
-		die_errno("could not read from stdin");
-	}
+	if (strbuf_read(&buf, 0, 0) < 0)
+		die_errno(_("could not read from stdin"));
 
-	/* Verify it for some basic sanity: it needs to start with
-	   "object <sha1>\ntype\ntagger " */
-	if (verify_tag(buf.buf, buf.len) < 0)
-		die("invalid tag signature file");
+	fsck_options.error_func = mktag_fsck_error_func;
+	fsck_set_msg_type(&fsck_options, "extraheaderentry", "warn");
+	/* config might set fsck.extraHeaderEntry=* again */
+	git_config(mktag_config, NULL);
+	if (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options,
+				&tagged_oid, &tagged_type))
+		die(_("tag on stdin did not pass our strict fsck check"));
+
+	if (verify_object_in_tag(&tagged_oid, &tagged_type))
+		die(_("tag on stdin did not refer to a valid object"));
 
 	if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
-		die("unable to write tag file");
+		die(_("unable to write tag file"));
 
 	strbuf_release(&buf);
-	printf("%s\n", oid_to_hex(&result));
+	puts(oid_to_hex(&result));
 	return 0;
 }
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 725dd04..b221d30 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -7,7 +7,7 @@
 #include "refs.h"
 #include "parse-options.h"
 #include "prio-queue.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "commit-slab.h"
 
 /*
@@ -390,10 +390,10 @@
 	}
 }
 
-static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
+static const struct object_id *nth_tip_table_ent(size_t ix, const void *table_)
 {
-	struct tip_table_entry *table = table_;
-	return table[ix].oid.hash;
+	const struct tip_table_entry *table = table_;
+	return &table[ix].oid;
 }
 
 static const char *get_exact_ref_match(const struct object *o)
@@ -408,8 +408,8 @@
 		tip_table.sorted = 1;
 	}
 
-	found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr,
-			 nth_tip_table_ent);
+	found = oid_pos(&o->oid, tip_table.table, tip_table.nr,
+			nth_tip_table_ent);
 	if (0 <= found)
 		return tip_table.table[found].refname;
 	return NULL;
diff --git a/builtin/notes.c b/builtin/notes.c
index 2987c08..08b8914 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -373,7 +373,7 @@
 				     git_notes_list_usage, 0);
 
 	if (1 < argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_list_usage, options);
 	}
 
@@ -428,7 +428,7 @@
 			     PARSE_OPT_KEEP_ARGV0);
 
 	if (2 < argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_add_usage, options);
 	}
 
@@ -506,7 +506,7 @@
 
 	if (from_stdin || rewrite_cmd) {
 		if (argc) {
-			error(_("too many parameters"));
+			error(_("too many arguments"));
 			usage_with_options(git_notes_copy_usage, options);
 		} else {
 			return notes_copy_from_stdin(force, rewrite_cmd);
@@ -514,11 +514,11 @@
 	}
 
 	if (argc < 1) {
-		error(_("too few parameters"));
+		error(_("too few arguments"));
 		usage_with_options(git_notes_copy_usage, options);
 	}
 	if (2 < argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_copy_usage, options);
 	}
 
@@ -595,7 +595,7 @@
 			     PARSE_OPT_KEEP_ARGV0);
 
 	if (2 < argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(usage, options);
 	}
 
@@ -662,7 +662,7 @@
 			     0);
 
 	if (1 < argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_show_usage, options);
 	}
 
@@ -812,7 +812,7 @@
 		error(_("must specify a notes ref to merge"));
 		usage_with_options(git_notes_merge_usage, options);
 	} else if (!do_merge && argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_merge_usage, options);
 	}
 
@@ -960,7 +960,7 @@
 			     0);
 
 	if (argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_prune_usage, options);
 	}
 
@@ -982,7 +982,7 @@
 			     git_notes_get_ref_usage, 0);
 
 	if (argc) {
-		error(_("too many parameters"));
+		error(_("too many arguments"));
 		usage_with_options(git_notes_get_ref_usage, options);
 	}
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 5617c01..6d62aaf 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -419,7 +419,7 @@
 {
 	struct packed_git *p = IN_PACK(entry);
 	struct pack_window *w_curs = NULL;
-	struct revindex_entry *revidx;
+	uint32_t pos;
 	off_t offset;
 	enum object_type type = oe_type(entry);
 	off_t datalen;
@@ -436,10 +436,15 @@
 					      type, entry_size);
 
 	offset = entry->in_pack_offset;
-	revidx = find_pack_revindex(p, offset);
-	datalen = revidx[1].offset - offset;
+	if (offset_to_pack_pos(p, offset, &pos) < 0)
+		die(_("write_reuse_object: could not locate %s, expected at "
+		      "offset %"PRIuMAX" in pack %s"),
+		    oid_to_hex(&entry->idx.oid), (uintmax_t)offset,
+		    p->pack_name);
+	datalen = pack_pos_to_offset(p, pos + 1) - offset;
 	if (!pack_to_stdout && p->index_version > 1 &&
-	    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+	    check_pack_crc(p, &w_curs, offset, datalen,
+			   pack_pos_to_index(p, pos))) {
 		error(_("bad packed object CRC for %s"),
 		      oid_to_hex(&entry->idx.oid));
 		unuse_pack(&w_curs);
@@ -629,7 +634,7 @@
 
 	if (entry)
 		entry->tagged = 1;
-	if (!peel_ref(path, &peeled)) {
+	if (!peel_iterated_oid(oid, &peeled)) {
 		entry = packlist_find(&to_pack, &peeled);
 		if (entry)
 			entry->tagged = 1;
@@ -863,8 +868,8 @@
 	enum object_type type;
 	unsigned long size;
 
-	offset = reuse_packfile->revindex[pos].offset;
-	next = reuse_packfile->revindex[pos + 1].offset;
+	offset = pack_pos_to_offset(reuse_packfile, pos);
+	next = pack_pos_to_offset(reuse_packfile, pos + 1);
 
 	record_reused_object(offset, offset - hashfile_total(out));
 
@@ -884,11 +889,17 @@
 
 		/* Convert to REF_DELTA if we must... */
 		if (!allow_ofs_delta) {
-			int base_pos = find_revindex_position(reuse_packfile, base_offset);
+			uint32_t base_pos;
 			struct object_id base_oid;
 
+			if (offset_to_pack_pos(reuse_packfile, base_offset, &base_pos) < 0)
+				die(_("expected object at offset %"PRIuMAX" "
+				      "in pack %s"),
+				    (uintmax_t)base_offset,
+				    reuse_packfile->pack_name);
+
 			nth_packed_object_id(&base_oid, reuse_packfile,
-					     reuse_packfile->revindex[base_pos].nr);
+					     pack_pos_to_index(reuse_packfile, base_pos));
 
 			len = encode_in_pack_object_header(header, sizeof(header),
 							   OBJ_REF_DELTA, size);
@@ -941,7 +952,7 @@
 		off_t to_write;
 
 		written = (pos * BITS_IN_EWORD);
-		to_write = reuse_packfile->revindex[written].offset
+		to_write = pack_pos_to_offset(reuse_packfile, written)
 			- sizeof(struct pack_header);
 
 		/* We're recording one chunk, not one object. */
@@ -1104,7 +1115,6 @@
 				stop_progress(&progress_state);
 
 				bitmap_writer_show_progress(progress);
-				bitmap_writer_reuse_bitmaps(&to_pack);
 				bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
 				bitmap_writer_build(&to_pack);
 				bitmap_writer_finish(written_list, nr_written,
@@ -1807,11 +1817,11 @@
 				goto give_up;
 			}
 			if (reuse_delta && !entry->preferred_base) {
-				struct revindex_entry *revidx;
-				revidx = find_pack_revindex(p, ofs);
-				if (!revidx)
+				uint32_t pos;
+				if (offset_to_pack_pos(p, ofs, &pos) < 0)
 					goto give_up;
-				if (!nth_packed_object_id(&base_ref, p, revidx->nr))
+				if (!nth_packed_object_id(&base_ref, p,
+							  pack_pos_to_index(p, pos)))
 					have_base = 1;
 			}
 			entry->in_pack_header_size = used + used_0;
@@ -2804,13 +2814,11 @@
 	}
 }
 
-static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
+static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data)
 {
 	struct object_id peeled;
 
-	if (starts_with(path, "refs/tags/") && /* is a tag? */
-	    !peel_ref(path, &peeled)    && /* peelable? */
-	    obj_is_packed(&peeled)) /* object packed? */
+	if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled))
 		add_tag_chain(oid);
 	return 0;
 }
@@ -2945,6 +2953,13 @@
 			    pack_idx_opts.version);
 		return 0;
 	}
+	if (!strcmp(k, "pack.writereverseindex")) {
+		if (git_config_bool(k, v))
+			pack_idx_opts.flags |= WRITE_REV;
+		else
+			pack_idx_opts.flags &= ~WRITE_REV;
+		return 0;
+	}
 	if (!strcmp(k, "uploadpack.blobpackfileuri")) {
 		struct configured_exclusion *ex = xmalloc(sizeof(*ex));
 		const char *oid_end, *pack_end;
@@ -3584,6 +3599,8 @@
 
 	reset_pack_idx_option(&pack_idx_opts);
 	git_config(git_pack_config, NULL);
+	if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
+		pack_idx_opts.flags |= WRITE_REV;
 
 	progress = isatty(2);
 	argc = parse_options(argc, argv, prefix, pack_objects_options,
@@ -3741,7 +3758,7 @@
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
-		for_each_ref(add_ref_tag, NULL);
+		for_each_tag_ref(add_ref_tag, NULL);
 	stop_progress(&progress_state);
 	trace2_region_leave("pack-objects", "enumerate-objects",
 			    the_repository);
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 9fcea3e..6e115a8 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -560,6 +560,7 @@
 int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
 {
 	int i;
+	int i_still_use_this = 0;
 	struct pack_list *min = NULL, *red, *pl;
 	struct llist *ignore;
 	struct object_id *oid;
@@ -586,12 +587,24 @@
 			alt_odb = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--i-still-use-this")) {
+			i_still_use_this = 1;
+			continue;
+		}
 		if (*arg == '-')
 			usage(pack_redundant_usage);
 		else
 			break;
 	}
 
+	if (!i_still_use_this) {
+		fputs(_("'git pack-redundant' is nominated for removal.\n"
+			"If you still use this command, please add an extra\n"
+			"option, '--i-still-use-this', on the command line\n"
+			"and let us know you still use it by sending an e-mail\n"
+			"to <git@vger.kernel.org>.  Thanks.\n"), stderr);
+	}
+
 	if (load_all_packs)
 		load_all();
 	else
diff --git a/builtin/pull.c b/builtin/pull.c
index aa56ebc..e8927fc 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -324,7 +324,7 @@
  * looks for the value of "pull.rebase". If both configuration keys do not
  * exist, returns REBASE_FALSE.
  */
-static enum rebase_type config_get_rebase(void)
+static enum rebase_type config_get_rebase(int *rebase_unspecified)
 {
 	struct branch *curr_branch = branch_get("HEAD");
 	const char *value;
@@ -344,20 +344,7 @@
 	if (!git_config_get_value("pull.rebase", &value))
 		return parse_config_rebase("pull.rebase", value, 1);
 
-	if (opt_verbosity >= 0 && !opt_ff) {
-		advise(_("Pulling without specifying how to reconcile divergent branches is\n"
-			 "discouraged. You can squelch this message by running one of the following\n"
-			 "commands sometime before your next pull:\n"
-			 "\n"
-			 "  git config pull.rebase false  # merge (the default strategy)\n"
-			 "  git config pull.rebase true   # rebase\n"
-			 "  git config pull.ff only       # fast-forward only\n"
-			 "\n"
-			 "You can replace \"git config\" with \"git config --global\" to set a default\n"
-			 "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
-			 "or --ff-only on the command line to override the configured default per\n"
-			 "invocation.\n"));
-	}
+	*rebase_unspecified = 1;
 
 	return REBASE_FALSE;
 }
@@ -924,6 +911,36 @@
 	return ret;
 }
 
+static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_head)
+{
+	int ret;
+	struct commit_list *list = NULL;
+	struct commit *merge_head, *head;
+
+	head = lookup_commit_reference(the_repository, orig_head);
+	commit_list_insert(head, &list);
+	merge_head = lookup_commit_reference(the_repository, orig_merge_head);
+	ret = repo_is_descendant_of(the_repository, merge_head, list);
+	free_commit_list(list);
+	return ret;
+}
+
+static void show_advice_pull_non_ff(void)
+{
+	advise(_("Pulling without specifying how to reconcile divergent branches is\n"
+		 "discouraged. You can squelch this message by running one of the following\n"
+		 "commands sometime before your next pull:\n"
+		 "\n"
+		 "  git config pull.rebase false  # merge (the default strategy)\n"
+		 "  git config pull.rebase true   # rebase\n"
+		 "  git config pull.ff only       # fast-forward only\n"
+		 "\n"
+		 "You can replace \"git config\" with \"git config --global\" to set a default\n"
+		 "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
+		 "or --ff-only on the command line to override the configured default per\n"
+		 "invocation.\n"));
+}
+
 int cmd_pull(int argc, const char **argv, const char *prefix)
 {
 	const char *repo, **refspecs;
@@ -931,6 +948,8 @@
 	struct object_id orig_head, curr_head;
 	struct object_id rebase_fork_point;
 	int autostash;
+	int rebase_unspecified = 0;
+	int can_ff;
 
 	if (!getenv("GIT_REFLOG_ACTION"))
 		set_reflog_message(argc, argv);
@@ -952,7 +971,7 @@
 		opt_ff = xstrdup_or_null(config_get_ff());
 
 	if (opt_rebase < 0)
-		opt_rebase = config_get_rebase();
+		opt_rebase = config_get_rebase(&rebase_unspecified);
 
 	if (read_cache_unmerged())
 		die_resolve_conflict("pull");
@@ -1026,6 +1045,13 @@
 	if (opt_rebase && merge_heads.nr > 1)
 		die(_("Cannot rebase onto multiple branches."));
 
+	can_ff = get_can_ff(&orig_head, &merge_heads.oid[0]);
+
+	if (rebase_unspecified && !opt_ff && !can_ff) {
+		if (opt_verbosity >= 0)
+			show_advice_pull_non_ff();
+	}
+
 	if (opt_rebase) {
 		int ret = 0;
 		int ran_ff = 0;
@@ -1040,22 +1066,12 @@
 		    submodule_touches_in_range(the_repository, &upstream, &curr_head))
 			die(_("cannot rebase with locally recorded submodule modifications"));
 		if (!autostash) {
-			struct commit_list *list = NULL;
-			struct commit *merge_head, *head;
-
-			head = lookup_commit_reference(the_repository,
-						       &orig_head);
-			commit_list_insert(head, &list);
-			merge_head = lookup_commit_reference(the_repository,
-							     &merge_heads.oid[0]);
-			if (repo_is_descendant_of(the_repository,
-						  merge_head, list)) {
+			if (can_ff) {
 				/* we can fast-forward this without invoking rebase */
 				opt_ff = "--ff-only";
 				ran_ff = 1;
 				ret = run_merge();
 			}
-			free_commit_list(list);
 		}
 		if (!ran_ff)
 			ret = run_rebase(&newbase, &upstream);
diff --git a/builtin/push.c b/builtin/push.c
index 03adb58..194967e 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -115,7 +115,7 @@
 			else
 				refspec_appendf(&rs, "refs/tags/%s", ref);
 		} else if (deleterefs) {
-			if (strchr(ref, ':'))
+			if (strchr(ref, ':') || !*ref)
 				die(_("--delete only accepts plain target ref names"));
 			refspec_appendf(&rs, ":%s", ref);
 		} else if (!strchr(ref, ':')) {
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 24c4162..78bc9fa 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -3,6 +3,7 @@
 #include "parse-options.h"
 #include "range-diff.h"
 #include "config.h"
+#include "revision.h"
 
 static const char * const builtin_range_diff_usage[] = {
 N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
@@ -13,18 +14,27 @@
 
 int cmd_range_diff(int argc, const char **argv, const char *prefix)
 {
-	int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
 	struct diff_options diffopt = { NULL };
 	struct strvec other_arg = STRVEC_INIT;
-	int simple_color = -1;
+	struct range_diff_options range_diff_opts = {
+		.creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT,
+		.diffopt = &diffopt,
+		.other_arg = &other_arg
+	};
+	int simple_color = -1, left_only = 0, right_only = 0;
 	struct option range_diff_options[] = {
-		OPT_INTEGER(0, "creation-factor", &creation_factor,
+		OPT_INTEGER(0, "creation-factor",
+			    &range_diff_opts.creation_factor,
 			    N_("Percentage by which creation is weighted")),
 		OPT_BOOL(0, "no-dual-color", &simple_color,
 			    N_("use simple diff colors")),
 		OPT_PASSTHRU_ARGV(0, "notes", &other_arg,
 				  N_("notes"), N_("passed to 'git log'"),
 				  PARSE_OPT_OPTARG),
+		OPT_BOOL(0, "left-only", &left_only,
+			 N_("only emit output related to the first range")),
+		OPT_BOOL(0, "right-only", &right_only,
+			 N_("only emit output related to the second range")),
 		OPT_END()
 	};
 	struct option *options;
@@ -46,12 +56,12 @@
 		diffopt.use_color = 1;
 
 	if (argc == 2) {
-		if (!strstr(argv[0], ".."))
-			die(_("no .. in range: '%s'"), argv[0]);
+		if (!is_range_diff_range(argv[0]))
+			die(_("not a commit range: '%s'"), argv[0]);
 		strbuf_addstr(&range1, argv[0]);
 
-		if (!strstr(argv[1], ".."))
-			die(_("no .. in range: '%s'"), argv[1]);
+		if (!is_range_diff_range(argv[1]))
+			die(_("not a commit range: '%s'"), argv[1]);
 		strbuf_addstr(&range2, argv[1]);
 	} else if (argc == 3) {
 		strbuf_addf(&range1, "%s..%s", argv[0], argv[1]);
@@ -81,8 +91,10 @@
 	}
 	FREE_AND_NULL(options);
 
-	res = show_range_diff(range1.buf, range2.buf, creation_factor,
-			      simple_color < 1, &diffopt, &other_arg);
+	range_diff_opts.dual_color = simple_color < 1;
+	range_diff_opts.left_only = left_only;
+	range_diff_opts.right_only = right_only;
+	res = show_range_diff(range1.buf, range2.buf, &range_diff_opts);
 
 	strvec_clear(&other_arg);
 	strbuf_release(&range1);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 840dbd7..de400f9 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -102,6 +102,7 @@
 	int reschedule_failed_exec;
 	int use_legacy_rebase;
 	int reapply_cherry_picks;
+	int fork_point;
 };
 
 #define REBASE_OPTIONS_INIT {			  	\
@@ -111,7 +112,8 @@
 		.default_backend = "merge",	  	\
 		.flags = REBASE_NO_QUIET, 		\
 		.git_am_opts = STRVEC_INIT,		\
-		.git_format_patch_opt = STRBUF_INIT	\
+		.git_format_patch_opt = STRBUF_INIT,	\
+		.fork_point = -1,			\
 	}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -1095,6 +1097,11 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.forkpoint")) {
+		opts->fork_point = git_config_bool(var, value) ? -1 : 0;
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.usebuiltin")) {
 		opts->use_legacy_rebase = !git_config_bool(var, value);
 		return 0;
@@ -1306,7 +1313,6 @@
 	const char *gpg_sign = NULL;
 	struct string_list exec = STRING_LIST_INIT_NODUP;
 	const char *rebase_merges = NULL;
-	int fork_point = -1;
 	struct string_list strategy_options = STRING_LIST_INIT_NODUP;
 	struct object_id squash_onto;
 	char *squash_onto_name = NULL;
@@ -1406,7 +1412,7 @@
 			N_("mode"),
 			N_("try to rebase merges instead of skipping them"),
 			PARSE_OPT_OPTARG, NULL, (intptr_t)""},
-		OPT_BOOL(0, "fork-point", &fork_point,
+		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
 		OPT_STRING('s', "strategy", &options.strategy,
 			   N_("strategy"), N_("use the given merge strategy")),
@@ -1494,7 +1500,7 @@
 			die(_("cannot combine '--keep-base' with '--root'"));
 	}
 
-	if (options.root && fork_point > 0)
+	if (options.root && options.fork_point > 0)
 		die(_("cannot combine '--root' with '--fork-point'"));
 
 	if (action != ACTION_NONE && !in_progress)
@@ -1840,8 +1846,8 @@
 								    NULL);
 			if (!options.upstream_name)
 				error_on_missing_default_upstream();
-			if (fork_point < 0)
-				fork_point = 1;
+			if (options.fork_point < 0)
+				options.fork_point = 1;
 		} else {
 			options.upstream_name = argv[0];
 			argc--;
@@ -1945,7 +1951,7 @@
 	} else
 		BUG("unexpected number of arguments left to parse");
 
-	if (fork_point > 0) {
+	if (options.fork_point > 0) {
 		struct commit *head =
 			lookup_commit_reference(the_repository,
 						&options.orig_head);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d49d050..d26040c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -764,7 +764,7 @@
 
 		memset(&sigcheck, '\0', sizeof(sigcheck));
 
-		bogs = parse_signature(push_cert.buf, push_cert.len);
+		bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
 		check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
 				push_cert.len - bogs, &sigcheck);
 
@@ -2050,7 +2050,7 @@
 		die("malformed push certificate %.*s", 100, push_cert->buf);
 	else
 		boc += 2;
-	eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+	eoc = push_cert->buf + parse_signed_buffer(push_cert->buf, push_cert->len);
 
 	while (boc < eoc) {
 		const char *eol = memchr(boc, '\n', eoc - boc);
@@ -2275,7 +2275,7 @@
 		status = start_command(&child);
 		if (status)
 			return "index-pack fork failed";
-		pack_lockfile = index_pack_lockfile(child.out);
+		pack_lockfile = index_pack_lockfile(child.out, NULL);
 		close(child.out);
 		status = finish_command(&child);
 		if (status)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index ca1d807..09541d1 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -602,6 +602,9 @@
 	 */
 	if (cb.cmd.stalefix) {
 		repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
+		cb.cmd.revs.do_not_die_on_missing_tree = 1;
+		cb.cmd.revs.ignore_missing = 1;
+		cb.cmd.revs.ignore_missing_links = 1;
 		if (flags & EXPIRE_REFLOGS_VERBOSE)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
diff --git a/builtin/repack.c b/builtin/repack.c
index 279be11..01440de 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -14,6 +14,7 @@
 #include "object-store.h"
 #include "promisor-remote.h"
 #include "shallow.h"
+#include "pack.h"
 
 static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
@@ -208,6 +209,7 @@
 } exts[] = {
 	{".pack"},
 	{".idx"},
+	{".rev", 1},
 	{".bitmap", 1},
 	{".promisor", 1},
 };
@@ -263,7 +265,7 @@
 	while (strbuf_getline_lf(&line, out) != EOF) {
 		struct string_list_item *item;
 		char *promisor_name;
-		int fd;
+
 		if (line.len != the_hash_algo->hexsz)
 			die(_("repack: Expecting full hex object ID lines only from pack-objects."));
 		item = string_list_append(names, line.buf);
@@ -281,10 +283,7 @@
 		 */
 		promisor_name = mkpathdup("%s-%s.promisor", packtmp,
 					  line.buf);
-		fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
-		if (fd < 0)
-			die_errno(_("unable to create '%s'"), promisor_name);
-		close(fd);
+		write_promisor_file(promisor_name, NULL, 0);
 
 		item->util = (void *)(uintptr_t)populate_pack_exts(item->string);
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 25c6c3b..b4d8ea0 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,6 +80,19 @@
 
 #define DEFAULT_OIDSET_SIZE     (16*1024)
 
+static int show_disk_usage;
+static off_t total_disk_usage;
+
+static off_t get_object_disk_usage(struct object *obj)
+{
+	off_t size;
+	struct object_info oi = OBJECT_INFO_INIT;
+	oi.disk_sizep = &size;
+	if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+		die(_("unable to get disk usage of %s"), oid_to_hex(&obj->oid));
+	return size;
+}
+
 static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit, void *data)
 {
@@ -88,6 +101,9 @@
 
 	display_progress(progress, ++progress_counter);
 
+	if (show_disk_usage)
+		total_disk_usage += get_object_disk_usage(&commit->object);
+
 	if (info->flags & REV_LIST_QUIET) {
 		finish_commit(commit);
 		return;
@@ -258,6 +274,8 @@
 	if (finish_object(obj, name, cb_data))
 		return;
 	display_progress(progress, ++progress_counter);
+	if (show_disk_usage)
+		total_disk_usage += get_object_disk_usage(obj);
 	if (info->flags & REV_LIST_QUIET)
 		return;
 
@@ -452,6 +470,23 @@
 	return 0;
 }
 
+static int try_bitmap_disk_usage(struct rev_info *revs,
+				 struct list_objects_filter_options *filter)
+{
+	struct bitmap_index *bitmap_git;
+
+	if (!show_disk_usage)
+		return -1;
+
+	bitmap_git = prepare_bitmap_walk(revs, filter);
+	if (!bitmap_git)
+		return -1;
+
+	printf("%"PRIuMAX"\n",
+	       (uintmax_t)get_disk_usage_from_bitmap(bitmap_git, revs));
+	return 0;
+}
+
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info revs;
@@ -584,6 +619,12 @@
 			continue;
 		}
 
+		if (!strcmp(arg, "--disk-usage")) {
+			show_disk_usage = 1;
+			info.flags |= REV_LIST_QUIET;
+			continue;
+		}
+
 		usage(rev_list_usage);
 
 	}
@@ -626,6 +667,8 @@
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, &filter_options))
 			return 0;
+		if (!try_bitmap_disk_usage(&revs, &filter_options))
+			return 0;
 		if (!try_bitmap_traversal(&revs, &filter_options))
 			return 0;
 	}
@@ -690,5 +733,8 @@
 			printf("%d\n", revs.count_left + revs.count_right);
 	}
 
+	if (show_disk_usage)
+		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
+
 	return 0;
 }
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 69ba732..85bad90 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -583,6 +583,75 @@
 	clear_ref_exclusion(&ref_excludes);
 }
 
+enum format_type {
+	/* We would like a relative path. */
+	FORMAT_RELATIVE,
+	/* We would like a canonical absolute path. */
+	FORMAT_CANONICAL,
+	/* We would like the default behavior. */
+	FORMAT_DEFAULT,
+};
+
+enum default_type {
+	/* Our default is a relative path. */
+	DEFAULT_RELATIVE,
+	/* Our default is a relative path if there's a shared root. */
+	DEFAULT_RELATIVE_IF_SHARED,
+	/* Our default is a canonical absolute path. */
+	DEFAULT_CANONICAL,
+	/* Our default is not to modify the item. */
+	DEFAULT_UNMODIFIED,
+};
+
+static void print_path(const char *path, const char *prefix, enum format_type format, enum default_type def)
+{
+	char *cwd = NULL;
+	/*
+	 * We don't ever produce a relative path if prefix is NULL, so set the
+	 * prefix to the current directory so that we can produce a relative
+	 * path whenever possible.  If we're using RELATIVE_IF_SHARED mode, then
+	 * we want an absolute path unless the two share a common prefix, so don't
+	 * set it in that case, since doing so causes a relative path to always
+	 * be produced if possible.
+	 */
+	if (!prefix && (format != FORMAT_DEFAULT || def != DEFAULT_RELATIVE_IF_SHARED))
+		prefix = cwd = xgetcwd();
+	if (format == FORMAT_DEFAULT && def == DEFAULT_UNMODIFIED) {
+		puts(path);
+	} else if (format == FORMAT_RELATIVE ||
+		  (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE)) {
+		/*
+		 * In order for relative_path to work as expected, we need to
+		 * make sure that both paths are absolute paths.  If we don't,
+		 * we can end up with an unexpected absolute path that the user
+		 * didn't want.
+		 */
+		struct strbuf buf = STRBUF_INIT, realbuf = STRBUF_INIT, prefixbuf = STRBUF_INIT;
+		if (!is_absolute_path(path)) {
+			strbuf_realpath_forgiving(&realbuf, path,  1);
+			path = realbuf.buf;
+		}
+		if (!is_absolute_path(prefix)) {
+			strbuf_realpath_forgiving(&prefixbuf, prefix, 1);
+			prefix = prefixbuf.buf;
+		}
+		puts(relative_path(path, prefix, &buf));
+		strbuf_release(&buf);
+		strbuf_release(&realbuf);
+		strbuf_release(&prefixbuf);
+	} else if (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE_IF_SHARED) {
+		struct strbuf buf = STRBUF_INIT;
+		puts(relative_path(path, prefix, &buf));
+		strbuf_release(&buf);
+	} else {
+		struct strbuf buf = STRBUF_INIT;
+		strbuf_realpath_forgiving(&buf, path, 1);
+		puts(buf.buf);
+		strbuf_release(&buf);
+	}
+	free(cwd);
+}
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
 	int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
@@ -596,6 +665,7 @@
 	struct strbuf buf = STRBUF_INIT;
 	const int hexsz = the_hash_algo->hexsz;
 	int seen_end_of_options = 0;
+	enum format_type format = FORMAT_DEFAULT;
 
 	if (argc > 1 && !strcmp("--parseopt", argv[1]))
 		return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -668,8 +738,9 @@
 				if (!argv[i + 1])
 					die("--git-path requires an argument");
 				strbuf_reset(&buf);
-				puts(relative_path(git_path("%s", argv[i + 1]),
-						   prefix, &buf));
+				print_path(git_path("%s", argv[i + 1]), prefix,
+						format,
+						DEFAULT_RELATIVE_IF_SHARED);
 				i++;
 				continue;
 			}
@@ -687,6 +758,16 @@
 					show(arg);
 				continue;
 			}
+			if (opt_with_value(arg, "--path-format", &arg)) {
+				if (!strcmp(arg, "absolute")) {
+					format = FORMAT_CANONICAL;
+				} else if (!strcmp(arg, "relative")) {
+					format = FORMAT_RELATIVE;
+				} else {
+					die("unknown argument to --path-format: %s", arg);
+				}
+				continue;
+			}
 			if (!strcmp(arg, "--default")) {
 				def = argv[++i];
 				if (!def)
@@ -807,7 +888,7 @@
 			if (!strcmp(arg, "--show-toplevel")) {
 				const char *work_tree = get_git_work_tree();
 				if (work_tree)
-					puts(work_tree);
+					print_path(work_tree, prefix, format, DEFAULT_UNMODIFIED);
 				else
 					die("this operation must be run in a work tree");
 				continue;
@@ -815,7 +896,7 @@
 			if (!strcmp(arg, "--show-superproject-working-tree")) {
 				struct strbuf superproject = STRBUF_INIT;
 				if (get_superproject_working_tree(&superproject))
-					puts(superproject.buf);
+					print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
 				strbuf_release(&superproject);
 				continue;
 			}
@@ -850,16 +931,18 @@
 				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
 				char *cwd;
 				int len;
+				enum format_type wanted = format;
 				if (arg[2] == 'g') {	/* --git-dir */
 					if (gitdir) {
-						puts(gitdir);
+						print_path(gitdir, prefix, format, DEFAULT_UNMODIFIED);
 						continue;
 					}
 					if (!prefix) {
-						puts(".git");
+						print_path(".git", prefix, format, DEFAULT_UNMODIFIED);
 						continue;
 					}
 				} else {		/* --absolute-git-dir */
+					wanted = FORMAT_CANONICAL;
 					if (!gitdir && !prefix)
 						gitdir = ".git";
 					if (gitdir) {
@@ -872,14 +955,14 @@
 				}
 				cwd = xgetcwd();
 				len = strlen(cwd);
-				printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
+				strbuf_reset(&buf);
+				strbuf_addf(&buf, "%s%s.git", cwd, len && cwd[len-1] != '/' ? "/" : "");
 				free(cwd);
+				print_path(buf.buf, prefix, wanted, DEFAULT_CANONICAL);
 				continue;
 			}
 			if (!strcmp(arg, "--git-common-dir")) {
-				strbuf_reset(&buf);
-				puts(relative_path(get_git_common_dir(),
-						   prefix, &buf));
+				print_path(get_git_common_dir(), prefix, format, DEFAULT_RELATIVE_IF_SHARED);
 				continue;
 			}
 			if (!strcmp(arg, "--is-inside-git-dir")) {
@@ -909,8 +992,7 @@
 				if (the_index.split_index) {
 					const struct object_id *oid = &the_index.split_index->base_oid;
 					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
-					strbuf_reset(&buf);
-					puts(relative_path(path, prefix, &buf));
+					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
 				continue;
 			}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 1c0b3a9..3e7ab1c 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -61,8 +61,7 @@
 	if (log->summary)
 		item->util = (void *)(UTIL_TO_INT(item) + 1);
 	else {
-		const char *dot3 = log->common_repo_prefix;
-		char *buffer, *p;
+		char *buffer;
 		struct strbuf subject = STRBUF_INIT;
 		const char *eol;
 
@@ -82,17 +81,6 @@
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (dot3) {
-			int dot3len = strlen(dot3);
-			if (dot3len > 5) {
-				while ((p = strstr(buffer, dot3)) != NULL) {
-					int taillen = strlen(p) - dot3len;
-					memcpy(p, "/.../", 5);
-					memmove(p + 5, p + dot3len, taillen + 1);
-				}
-			}
-		}
-
 		if (item->util == NULL)
 			item->util = xcalloc(1, sizeof(struct string_list));
 		string_list_append(item->util, buffer);
@@ -342,7 +330,7 @@
 {
 	memset(log, 0, sizeof(*log));
 
-	read_mailmap(&log->mailmap, &log->common_repo_prefix);
+	read_mailmap(&log->mailmap);
 
 	log->list.strdup_strings = 1;
 	log->wrap = DEFAULT_WRAPLEN;
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index ae60b4a..7f8a533 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -40,7 +40,7 @@
 	if (!deref_tags)
 		return;
 
-	if (!peel_ref(refname, &peeled)) {
+	if (!peel_iterated_oid(oid, &peeled)) {
 		hex = find_unique_abbrev(&peeled, abbrev);
 		printf("%s %s^{}\n", hex, refname);
 	}
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index e3140db..2306a9a 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -22,11 +22,6 @@
 	NULL
 };
 
-static char *get_sparse_checkout_filename(void)
-{
-	return git_pathdup("info/sparse-checkout");
-}
-
 static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
 {
 	int i;
diff --git a/builtin/stash.c b/builtin/stash.c
index 9bc85f9..ba774cc 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -87,7 +87,7 @@
 	NULL
 };
 
-static const char *ref_stash = "refs/stash";
+static const char ref_stash[] = "refs/stash";
 static struct strbuf stash_index_path = STRBUF_INIT;
 
 /*
@@ -222,7 +222,7 @@
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 
 	if (argc)
-		return error(_("git stash clear with parameters is "
+		return error(_("git stash clear with arguments is "
 			       "unimplemented"));
 
 	return do_clear_stash();
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index c2bd882..9d505a6 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1257,7 +1257,7 @@
 	git_config(git_diff_basic_config, NULL);
 	init_revisions(&rev, info->prefix);
 	rev.abbrev = 0;
-	precompose_argv(diff_args.nr, diff_args.v);
+	precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
 	setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
 	rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
 	rev.diffopt.format_callback = submodule_summary_callback;
diff --git a/builtin/tag.c b/builtin/tag.c
index 24d35b7..d403417 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -72,10 +72,10 @@
 }
 
 typedef int (*each_tag_name_fn)(const char *name, const char *ref,
-				const struct object_id *oid, const void *cb_data);
+				const struct object_id *oid, void *cb_data);
 
 static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
-			     const void *cb_data)
+			     void *cb_data)
 {
 	const char **p;
 	struct strbuf ref = STRBUF_INIT;
@@ -97,18 +97,42 @@
 	return had_error;
 }
 
-static int delete_tag(const char *name, const char *ref,
-		      const struct object_id *oid, const void *cb_data)
+static int collect_tags(const char *name, const char *ref,
+			const struct object_id *oid, void *cb_data)
 {
-	if (delete_ref(NULL, ref, oid, 0))
-		return 1;
-	printf(_("Deleted tag '%s' (was %s)\n"), name,
-	       find_unique_abbrev(oid, DEFAULT_ABBREV));
+	struct string_list *ref_list = cb_data;
+
+	string_list_append(ref_list, ref);
+	ref_list->items[ref_list->nr - 1].util = oiddup(oid);
 	return 0;
 }
 
+static int delete_tags(const char **argv)
+{
+	int result;
+	struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+
+	result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
+	if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+		result = 1;
+
+	for_each_string_list_item(item, &refs_to_delete) {
+		const char *name = item->string;
+		struct object_id *oid = item->util;
+		if (!ref_exists(name))
+			printf(_("Deleted tag '%s' (was %s)\n"),
+				item->string + 10,
+				find_unique_abbrev(oid, DEFAULT_ABBREV));
+
+		free(oid);
+	}
+	string_list_clear(&refs_to_delete, 0);
+	return result;
+}
+
 static int verify_tag(const char *name, const char *ref,
-		      const struct object_id *oid, const void *cb_data)
+		      const struct object_id *oid, void *cb_data)
 {
 	int flags;
 	const struct ref_format *format = cb_data;
@@ -174,11 +198,17 @@
 {
 	unsigned long size;
 	enum object_type type;
-	char *buf, *sp;
+	char *buf, *sp, *orig;
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
 
-	buf = read_object_file(oid, &type, &size);
+	orig = buf = read_object_file(oid, &type, &size);
 	if (!buf)
 		return;
+	if (parse_signature(buf, size, &payload, &signature)) {
+		buf = payload.buf;
+		size = payload.len;
+	}
 	/* skip header */
 	sp = strstr(buf, "\n\n");
 
@@ -187,9 +217,11 @@
 		return;
 	}
 	sp += 2; /* skip the 2 LFs */
-	write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
+	write_or_die(fd, sp, buf + size - sp);
 
-	free(buf);
+	free(orig);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
 }
 
 static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
@@ -512,7 +544,7 @@
 	if (filter.reachable_from || filter.unreachable_from)
 		die(_("--merged and --no-merged options are only allowed in list mode"));
 	if (cmdmode == 'd')
-		return for_each_tag_name(argv, delete_tag, NULL);
+		return delete_tags(argv);
 	if (cmdmode == 'v') {
 		if (format.format && verify_ref_format(&format))
 			usage_with_options(git_tag_usage, options);
@@ -540,7 +572,7 @@
 
 	object_ref = argc == 2 ? argv[1] : "HEAD";
 	if (argc > 2)
-		die(_("too many params"));
+		die(_("too many arguments"));
 
 	if (get_oid(object_ref, &object))
 		die(_("Failed to resolve '%s' as a valid ref."), object_ref);
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 197fd24..1cd5c20 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -12,6 +12,7 @@
 #include "submodule.h"
 #include "utf8.h"
 #include "worktree.h"
+#include "quote.h"
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<commit-ish>]"),
@@ -67,79 +68,6 @@
 	rmdir(git_path("worktrees")); /* ignore failed removal */
 }
 
-/*
- * Return true if worktree entry should be pruned, along with the reason for
- * pruning. Otherwise, return false and the worktree's path, or NULL if it
- * cannot be determined. Caller is responsible for freeing returned path.
- */
-static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath)
-{
-	struct stat st;
-	char *path;
-	int fd;
-	size_t len;
-	ssize_t read_result;
-
-	*wtpath = NULL;
-	if (!is_directory(git_path("worktrees/%s", id))) {
-		strbuf_addstr(reason, _("not a valid directory"));
-		return 1;
-	}
-	if (file_exists(git_path("worktrees/%s/locked", id)))
-		return 0;
-	if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
-		strbuf_addstr(reason, _("gitdir file does not exist"));
-		return 1;
-	}
-	fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
-	if (fd < 0) {
-		strbuf_addf(reason, _("unable to read gitdir file (%s)"),
-			    strerror(errno));
-		return 1;
-	}
-	len = xsize_t(st.st_size);
-	path = xmallocz(len);
-
-	read_result = read_in_full(fd, path, len);
-	if (read_result < 0) {
-		strbuf_addf(reason, _("unable to read gitdir file (%s)"),
-			    strerror(errno));
-		close(fd);
-		free(path);
-		return 1;
-	}
-	close(fd);
-
-	if (read_result != len) {
-		strbuf_addf(reason,
-			    _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
-			    (uintmax_t)len, (uintmax_t)read_result);
-		free(path);
-		return 1;
-	}
-	while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
-		len--;
-	if (!len) {
-		strbuf_addstr(reason, _("invalid gitdir file"));
-		free(path);
-		return 1;
-	}
-	path[len] = '\0';
-	if (!file_exists(path)) {
-		if (stat(git_path("worktrees/%s/index", id), &st) ||
-		    st.st_mtime <= expire) {
-			strbuf_addstr(reason, _("gitdir file points to non-existent location"));
-			free(path);
-			return 1;
-		} else {
-			*wtpath = path;
-			return 0;
-		}
-	}
-	*wtpath = path;
-	return 0;
-}
-
 static void prune_worktree(const char *id, const char *reason)
 {
 	if (show_only || verbose)
@@ -195,7 +123,7 @@
 		if (is_dot_or_dotdot(d->d_name))
 			continue;
 		strbuf_reset(&reason);
-		if (should_prune_worktree(d->d_name, &reason, &path))
+		if (should_prune_worktree(d->d_name, &reason, &path, expire))
 			prune_worktree(d->d_name, reason.buf);
 		else if (path)
 			string_list_append(&kept, path)->util = xstrdup(d->d_name);
@@ -642,6 +570,8 @@
 
 static void show_worktree_porcelain(struct worktree *wt)
 {
+	const char *reason;
+
 	printf("worktree %s\n", wt->path);
 	if (wt->is_bare)
 		printf("bare\n");
@@ -652,6 +582,20 @@
 		else if (wt->head_ref)
 			printf("branch %s\n", wt->head_ref);
 	}
+
+	reason = worktree_lock_reason(wt);
+	if (reason && *reason) {
+		struct strbuf sb = STRBUF_INIT;
+		quote_c_style(reason, &sb, NULL, 0);
+		printf("locked %s\n", sb.buf);
+		strbuf_release(&sb);
+	} else if (reason)
+		printf("locked\n");
+
+	reason = worktree_prune_reason(wt, expire);
+	if (reason)
+		printf("prunable %s\n", reason);
+
 	printf("\n");
 }
 
@@ -660,6 +604,7 @@
 	struct strbuf sb = STRBUF_INIT;
 	int cur_path_len = strlen(wt->path);
 	int path_adj = cur_path_len - utf8_strwidth(wt->path);
+	const char *reason;
 
 	strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
 	if (wt->is_bare)
@@ -677,9 +622,18 @@
 			strbuf_addstr(&sb, "(error)");
 	}
 
-	if (!is_main_worktree(wt) && worktree_lock_reason(wt))
+	reason = worktree_lock_reason(wt);
+	if (verbose && reason && *reason)
+		strbuf_addf(&sb, "\n\tlocked: %s", reason);
+	else if (reason)
 		strbuf_addstr(&sb, " locked");
 
+	reason = worktree_prune_reason(wt, expire);
+	if (verbose && reason)
+		strbuf_addf(&sb, "\n\tprunable: %s", reason);
+	else if (reason)
+		strbuf_addstr(&sb, " prunable");
+
 	printf("%s\n", sb.buf);
 	strbuf_release(&sb);
 }
@@ -723,12 +677,18 @@
 
 	struct option options[] = {
 		OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+		OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
+		OPT_EXPIRY_DATE(0, "expire", &expire,
+				N_("add 'prunable' annotation to worktrees older than <time>")),
 		OPT_END()
 	};
 
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
+	else if (verbose && porcelain)
+		die(_("--verbose and --porcelain are mutually exclusive"));
 	else {
 		struct worktree **worktrees = get_worktrees();
 		int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
@@ -1052,10 +1012,10 @@
 	int rc = 0;
 
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
-	repair_worktrees(report_repair, &rc);
 	p = ac > 0 ? av : self;
 	for (; *p; p++)
 		repair_worktree_at_path(*p, report_repair, &rc);
+	repair_worktrees(report_repair, &rc);
 	return rc;
 }
 
diff --git a/bundle.c b/bundle.c
index cb0e593..693d619 100644
--- a/bundle.c
+++ b/bundle.c
@@ -338,48 +338,6 @@
 	return 0;
 }
 
-static int compute_and_write_prerequisites(int bundle_fd,
-					   struct rev_info *revs,
-					   int argc, const char **argv)
-{
-	struct child_process rls = CHILD_PROCESS_INIT;
-	struct strbuf buf = STRBUF_INIT;
-	FILE *rls_fout;
-	int i;
-
-	strvec_pushl(&rls.args,
-		     "rev-list", "--boundary", "--pretty=oneline",
-		     NULL);
-	for (i = 1; i < argc; i++)
-		strvec_push(&rls.args, argv[i]);
-	rls.out = -1;
-	rls.git_cmd = 1;
-	if (start_command(&rls))
-		return -1;
-	rls_fout = xfdopen(rls.out, "r");
-	while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) {
-		struct object_id oid;
-		if (buf.len > 0 && buf.buf[0] == '-') {
-			write_or_die(bundle_fd, buf.buf, buf.len);
-			if (!get_oid_hex(buf.buf + 1, &oid)) {
-				struct object *object = parse_object_or_die(&oid,
-									    buf.buf);
-				object->flags |= UNINTERESTING;
-				add_pending_object(revs, object, buf.buf);
-			}
-		} else if (!get_oid_hex(buf.buf, &oid)) {
-			struct object *object = parse_object_or_die(&oid,
-								    buf.buf);
-			object->flags |= SHOWN;
-		}
-	}
-	strbuf_release(&buf);
-	fclose(rls_fout);
-	if (finish_command(&rls))
-		return error(_("rev-list died"));
-	return 0;
-}
-
 /*
  * Write out bundle refs based on the tips already
  * parsed into revs.pending. As a side effect, may
@@ -474,6 +432,38 @@
 	return ref_count;
 }
 
+struct bundle_prerequisites_info {
+	struct object_array *pending;
+	int fd;
+};
+
+static void write_bundle_prerequisites(struct commit *commit, void *data)
+{
+	struct bundle_prerequisites_info *bpi = data;
+	struct object *object;
+	struct pretty_print_context ctx = { 0 };
+	struct strbuf buf = STRBUF_INIT;
+
+	if (!(commit->object.flags & BOUNDARY))
+		return;
+	strbuf_addf(&buf, "-%s ", oid_to_hex(&commit->object.oid));
+	write_or_die(bpi->fd, buf.buf, buf.len);
+
+	ctx.fmt = CMIT_FMT_ONELINE;
+	ctx.output_encoding = get_log_output_encoding();
+	strbuf_reset(&buf);
+	pretty_print_commit(&ctx, commit, &buf);
+	strbuf_trim(&buf);
+
+	object = (struct object *)commit;
+	object->flags |= UNINTERESTING;
+	add_object_array_with_path(object, buf.buf, bpi->pending, S_IFINVALID,
+				   NULL);
+	strbuf_addch(&buf, '\n');
+	write_or_die(bpi->fd, buf.buf, buf.len);
+	strbuf_release(&buf);
+}
+
 int create_bundle(struct repository *r, const char *path,
 		  int argc, const char **argv, struct strvec *pack_options, int version)
 {
@@ -481,8 +471,10 @@
 	int bundle_fd = -1;
 	int bundle_to_stdout;
 	int ref_count = 0;
-	struct rev_info revs;
+	struct rev_info revs, revs_copy;
 	int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
+	struct bundle_prerequisites_info bpi;
+	int i;
 
 	bundle_to_stdout = !strcmp(path, "-");
 	if (bundle_to_stdout)
@@ -512,10 +504,6 @@
 	save_commit_buffer = 0;
 	repo_init_revisions(r, &revs, NULL);
 
-	/* write prerequisites */
-	if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
-		goto err;
-
 	argc = setup_revisions(argc, argv, &revs, NULL);
 
 	if (argc > 1) {
@@ -523,16 +511,37 @@
 		goto err;
 	}
 
-	object_array_remove_duplicates(&revs.pending);
+	/* save revs.pending in revs_copy for later use */
+	memcpy(&revs_copy, &revs, sizeof(revs));
+	revs_copy.pending.nr = 0;
+	revs_copy.pending.alloc = 0;
+	revs_copy.pending.objects = NULL;
+	for (i = 0; i < revs.pending.nr; i++) {
+		struct object_array_entry *e = revs.pending.objects + i;
+		if (e)
+			add_object_array_with_path(e->item, e->name,
+						   &revs_copy.pending,
+						   e->mode, e->path);
+	}
 
-	ref_count = write_bundle_refs(bundle_fd, &revs);
+	/* write prerequisites */
+	revs.boundary = 1;
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	bpi.fd = bundle_fd;
+	bpi.pending = &revs_copy.pending;
+	traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
+	object_array_remove_duplicates(&revs_copy.pending);
+
+	/* write bundle refs */
+	ref_count = write_bundle_refs(bundle_fd, &revs_copy);
 	if (!ref_count)
 		die(_("Refusing to create empty bundle."));
 	else if (ref_count < 0)
 		goto err;
 
 	/* write pack */
-	if (write_pack_data(bundle_fd, &revs, pack_options))
+	if (write_pack_data(bundle_fd, &revs_copy, pack_options))
 		goto err;
 
 	if (!bundle_to_stdout) {
diff --git a/cache-tree.c b/cache-tree.c
index a537a80..2fb483d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -45,7 +45,7 @@
 	return memcmp(one, two, onelen);
 }
 
-static int subtree_pos(struct cache_tree *it, const char *path, int pathlen)
+int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen)
 {
 	struct cache_tree_sub **down = it->down;
 	int lo, hi;
@@ -72,7 +72,7 @@
 					   int create)
 {
 	struct cache_tree_sub *down;
-	int pos = subtree_pos(it, path, pathlen);
+	int pos = cache_tree_subtree_pos(it, path, pathlen);
 	if (0 <= pos)
 		return it->down[pos];
 	if (!create)
@@ -123,7 +123,7 @@
 	it->entry_count = -1;
 	if (!*slash) {
 		int pos;
-		pos = subtree_pos(it, path, namelen);
+		pos = cache_tree_subtree_pos(it, path, namelen);
 		if (0 <= pos) {
 			cache_tree_free(&it->down[pos]->cache_tree);
 			free(it->down[pos]);
@@ -151,16 +151,15 @@
 		istate->cache_changed |= CACHE_TREE_CHANGED;
 }
 
-static int verify_cache(struct cache_entry **cache,
-			int entries, int flags)
+static int verify_cache(struct index_state *istate, int flags)
 {
-	int i, funny;
+	unsigned i, funny;
 	int silent = flags & WRITE_TREE_SILENT;
 
 	/* Verify that the tree is merged */
 	funny = 0;
-	for (i = 0; i < entries; i++) {
-		const struct cache_entry *ce = cache[i];
+	for (i = 0; i < istate->cache_nr; i++) {
+		const struct cache_entry *ce = istate->cache[i];
 		if (ce_stage(ce)) {
 			if (silent)
 				return -1;
@@ -180,17 +179,19 @@
 	 * stage 0 entries.
 	 */
 	funny = 0;
-	for (i = 0; i < entries - 1; i++) {
+	for (i = 0; i + 1 < istate->cache_nr; i++) {
 		/* path/file always comes after path because of the way
 		 * the cache is sorted.  Also path can appear only once,
 		 * which means conflicting one would immediately follow.
 		 */
-		const char *this_name = cache[i]->name;
-		const char *next_name = cache[i+1]->name;
-		int this_len = strlen(this_name);
-		if (this_len < strlen(next_name) &&
-		    strncmp(this_name, next_name, this_len) == 0 &&
-		    next_name[this_len] == '/') {
+		const struct cache_entry *this_ce = istate->cache[i];
+		const struct cache_entry *next_ce = istate->cache[i + 1];
+		const char *this_name = this_ce->name;
+		const char *next_name = next_ce->name;
+		int this_len = ce_namelen(this_ce);
+		if (this_len < ce_namelen(next_ce) &&
+		    next_name[this_len] == '/' &&
+		    strncmp(this_name, next_name, this_len) == 0) {
 			if (10 < ++funny) {
 				fprintf(stderr, "...\n");
 				break;
@@ -434,15 +435,21 @@
 
 int cache_tree_update(struct index_state *istate, int flags)
 {
-	struct cache_tree *it = istate->cache_tree;
-	struct cache_entry **cache = istate->cache;
-	int entries = istate->cache_nr;
-	int skip, i = verify_cache(cache, entries, flags);
+	int skip, i;
+
+	i = verify_cache(istate, flags);
 
 	if (i)
 		return i;
+
+	if (!istate->cache_tree)
+		istate->cache_tree = cache_tree();
+
 	trace_performance_enter();
-	i = update_one(it, cache, entries, "", 0, &skip, flags);
+	trace2_region_enter("cache_tree", "update", the_repository);
+	i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
+		       "", 0, &skip, flags);
+	trace2_region_leave("cache_tree", "update", the_repository);
 	trace_performance_leave("cache_tree_update");
 	if (i < 0)
 		return i;
@@ -492,7 +499,9 @@
 
 void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
 {
+	trace2_region_enter("cache_tree", "write", the_repository);
 	write_one(sb, root, "", 0);
+	trace2_region_leave("cache_tree", "write", the_repository);
 }
 
 static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
@@ -581,9 +590,16 @@
 
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
 {
+	struct cache_tree *result;
+
 	if (buffer[0])
 		return NULL; /* not the whole tree */
-	return read_one(&buffer, &size);
+
+	trace2_region_enter("cache_tree", "read", the_repository);
+	result = read_one(&buffer, &size);
+	trace2_region_leave("cache_tree", "read", the_repository);
+
+	return result;
 }
 
 static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
@@ -622,9 +638,6 @@
 		cache_tree_valid = 0;
 	}
 
-	if (!index_state->cache_tree)
-		index_state->cache_tree = cache_tree();
-
 	if (!cache_tree_valid && cache_tree_update(index_state, flags) < 0)
 		return WRITE_TREE_UNMERGED_INDEX;
 
@@ -733,10 +746,13 @@
 		      struct index_state *istate,
 		      struct tree *tree)
 {
+	trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
 	cache_tree_free(&istate->cache_tree);
 	istate->cache_tree = cache_tree();
+
 	prime_cache_tree_rec(r, istate->cache_tree, tree);
 	istate->cache_changed |= CACHE_TREE_CHANGED;
+	trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
 }
 
 /*
diff --git a/cache-tree.h b/cache-tree.h
index 639bfa5..8efecce 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -27,6 +27,8 @@
 void cache_tree_invalidate_path(struct index_state *, const char *);
 struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
 
+int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen);
+
 void cache_tree_write(struct strbuf *, struct cache_tree *root);
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
 
diff --git a/cache.h b/cache.h
index 0a0b32f..6fda809 100644
--- a/cache.h
+++ b/cache.h
@@ -328,6 +328,7 @@
 	struct ewah_bitmap *fsmonitor_dirty;
 	struct mem_pool *ce_mem_pool;
 	struct progress *progress;
+	struct repository *repo;
 };
 
 /* Name hashing */
@@ -472,6 +473,7 @@
 #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
 #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 #define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
+#define CONFIG_COUNT_ENVIRONMENT "GIT_CONFIG_COUNT"
 #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
@@ -1231,6 +1233,8 @@
 int is_directory(const char *);
 char *strbuf_realpath(struct strbuf *resolved, const char *path,
 		      int die_on_error);
+char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
+				int die_on_error);
 char *real_pathdup(const char *path, int die_on_error);
 const char *absolute_path(const char *path);
 char *absolute_pathdup(const char *path);
diff --git a/chunk-format.c b/chunk-format.c
new file mode 100644
index 0000000..da191e5
--- /dev/null
+++ b/chunk-format.c
@@ -0,0 +1,179 @@
+#include "cache.h"
+#include "chunk-format.h"
+#include "csum-file.h"
+
+/*
+ * When writing a chunk-based file format, collect the chunks in
+ * an array of chunk_info structs. The size stores the _expected_
+ * amount of data that will be written by write_fn.
+ */
+struct chunk_info {
+	uint32_t id;
+	uint64_t size;
+	chunk_write_fn write_fn;
+
+	const void *start;
+};
+
+struct chunkfile {
+	struct hashfile *f;
+
+	struct chunk_info *chunks;
+	size_t chunks_nr;
+	size_t chunks_alloc;
+};
+
+struct chunkfile *init_chunkfile(struct hashfile *f)
+{
+	struct chunkfile *cf = xcalloc(1, sizeof(*cf));
+	cf->f = f;
+	return cf;
+}
+
+void free_chunkfile(struct chunkfile *cf)
+{
+	if (!cf)
+		return;
+	free(cf->chunks);
+	free(cf);
+}
+
+int get_num_chunks(struct chunkfile *cf)
+{
+	return cf->chunks_nr;
+}
+
+void add_chunk(struct chunkfile *cf,
+	       uint32_t id,
+	       size_t size,
+	       chunk_write_fn fn)
+{
+	ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);
+
+	cf->chunks[cf->chunks_nr].id = id;
+	cf->chunks[cf->chunks_nr].write_fn = fn;
+	cf->chunks[cf->chunks_nr].size = size;
+	cf->chunks_nr++;
+}
+
+int write_chunkfile(struct chunkfile *cf, void *data)
+{
+	int i;
+	uint64_t cur_offset = hashfile_total(cf->f);
+
+	/* Add the table of contents to the current offset */
+	cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;
+
+	for (i = 0; i < cf->chunks_nr; i++) {
+		hashwrite_be32(cf->f, cf->chunks[i].id);
+		hashwrite_be64(cf->f, cur_offset);
+
+		cur_offset += cf->chunks[i].size;
+	}
+
+	/* Trailing entry marks the end of the chunks */
+	hashwrite_be32(cf->f, 0);
+	hashwrite_be64(cf->f, cur_offset);
+
+	for (i = 0; i < cf->chunks_nr; i++) {
+		off_t start_offset = hashfile_total(cf->f);
+		int result = cf->chunks[i].write_fn(cf->f, data);
+
+		if (result)
+			return result;
+
+		if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)
+			BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
+			    cf->chunks[i].size, cf->chunks[i].id,
+			    hashfile_total(cf->f) - start_offset);
+	}
+
+	return 0;
+}
+
+int read_table_of_contents(struct chunkfile *cf,
+			   const unsigned char *mfile,
+			   size_t mfile_size,
+			   uint64_t toc_offset,
+			   int toc_length)
+{
+	int i;
+	uint32_t chunk_id;
+	const unsigned char *table_of_contents = mfile + toc_offset;
+
+	ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);
+
+	while (toc_length--) {
+		uint64_t chunk_offset, next_chunk_offset;
+
+		chunk_id = get_be32(table_of_contents);
+		chunk_offset = get_be64(table_of_contents + 4);
+
+		if (!chunk_id) {
+			error(_("terminating chunk id appears earlier than expected"));
+			return 1;
+		}
+
+		table_of_contents += CHUNK_TOC_ENTRY_SIZE;
+		next_chunk_offset = get_be64(table_of_contents + 4);
+
+		if (next_chunk_offset < chunk_offset ||
+		    next_chunk_offset > mfile_size - the_hash_algo->rawsz) {
+			error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),
+			      chunk_offset, next_chunk_offset);
+			return -1;
+		}
+
+		for (i = 0; i < cf->chunks_nr; i++) {
+			if (cf->chunks[i].id == chunk_id) {
+				error(_("duplicate chunk ID %"PRIx32" found"),
+					chunk_id);
+				return -1;
+			}
+		}
+
+		cf->chunks[cf->chunks_nr].id = chunk_id;
+		cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;
+		cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;
+		cf->chunks_nr++;
+	}
+
+	chunk_id = get_be32(table_of_contents);
+	if (chunk_id) {
+		error(_("final chunk has non-zero id %"PRIx32""), chunk_id);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int pair_chunk_fn(const unsigned char *chunk_start,
+			 size_t chunk_size,
+			 void *data)
+{
+	const unsigned char **p = data;
+	*p = chunk_start;
+	return 0;
+}
+
+int pair_chunk(struct chunkfile *cf,
+	       uint32_t chunk_id,
+	       const unsigned char **p)
+{
+	return read_chunk(cf, chunk_id, pair_chunk_fn, p);
+}
+
+int read_chunk(struct chunkfile *cf,
+	       uint32_t chunk_id,
+	       chunk_read_fn fn,
+	       void *data)
+{
+	int i;
+
+	for (i = 0; i < cf->chunks_nr; i++) {
+		if (cf->chunks[i].id == chunk_id)
+			return fn(cf->chunks[i].start, cf->chunks[i].size, data);
+	}
+
+	return CHUNK_NOT_FOUND;
+}
diff --git a/chunk-format.h b/chunk-format.h
new file mode 100644
index 0000000..9ccbe00
--- /dev/null
+++ b/chunk-format.h
@@ -0,0 +1,68 @@
+#ifndef CHUNK_FORMAT_H
+#define CHUNK_FORMAT_H
+
+#include "git-compat-util.h"
+
+struct hashfile;
+struct chunkfile;
+
+#define CHUNK_TOC_ENTRY_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
+
+/*
+ * Initialize a 'struct chunkfile' for writing _or_ reading a file
+ * with the chunk format.
+ *
+ * If writing a file, supply a non-NULL 'struct hashfile *' that will
+ * be used to write.
+ *
+ * If reading a file, use a NULL 'struct hashfile *' and then call
+ * read_table_of_contents(). Supply the memory-mapped data to the
+ * pair_chunk() or read_chunk() methods, as appropriate.
+ *
+ * DO NOT MIX THESE MODES. Use different 'struct chunkfile' instances
+ * for reading and writing.
+ */
+struct chunkfile *init_chunkfile(struct hashfile *f);
+void free_chunkfile(struct chunkfile *cf);
+int get_num_chunks(struct chunkfile *cf);
+typedef int (*chunk_write_fn)(struct hashfile *f, void *data);
+void add_chunk(struct chunkfile *cf,
+	       uint32_t id,
+	       size_t size,
+	       chunk_write_fn fn);
+int write_chunkfile(struct chunkfile *cf, void *data);
+
+int read_table_of_contents(struct chunkfile *cf,
+			   const unsigned char *mfile,
+			   size_t mfile_size,
+			   uint64_t toc_offset,
+			   int toc_length);
+
+#define CHUNK_NOT_FOUND (-2)
+
+/*
+ * Find 'chunk_id' in the given chunkfile and assign the
+ * given pointer to the position in the mmap'd file where
+ * that chunk begins.
+ *
+ * Returns CHUNK_NOT_FOUND if the chunk does not exist.
+ */
+int pair_chunk(struct chunkfile *cf,
+	       uint32_t chunk_id,
+	       const unsigned char **p);
+
+typedef int (*chunk_read_fn)(const unsigned char *chunk_start,
+			     size_t chunk_size, void *data);
+/*
+ * Find 'chunk_id' in the given chunkfile and call the
+ * given chunk_read_fn method with the information for
+ * that chunk.
+ *
+ * Returns CHUNK_NOT_FOUND if the chunk does not exist.
+ */
+int read_chunk(struct chunkfile *cf,
+	       uint32_t chunk_id,
+	       chunk_read_fn fn,
+	       void *data);
+
+#endif
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 0b1184e..67852d0 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -72,7 +72,7 @@
 	test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
 	sudo gem install --version 1.5.8 asciidoctor
 	;;
-linux-gcc-4.8|GETTEXT_POISON)
+linux-gcc-default|linux-gcc-4.8)
 	sudo apt-get -q update
 	sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
 	;;
diff --git a/ci/lib.sh b/ci/lib.sh
index 38c0eac..d848c03 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -220,8 +220,7 @@
 	# Travis CI OS X
 	export GIT_SKIP_TESTS="t9810 t9816"
 	;;
-GETTEXT_POISON)
-	export GIT_TEST_GETTEXT_POISON=true
+linux-gcc-default)
 	;;
 Linux32)
 	CC=gcc
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 6c27b88..a66b5e8 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -13,6 +13,7 @@
 make
 case "$jobname" in
 linux-gcc)
+	export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 	make test
 	export GIT_TEST_SPLIT_INDEX=yes
 	export GIT_TEST_FULL_IN_PACK_ARRAY=true
@@ -22,6 +23,8 @@
 	export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
 	export GIT_TEST_MULTI_PACK_INDEX=1
 	export GIT_TEST_ADD_I_USE_BUILTIN=1
+	export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+	export GIT_TEST_WRITE_REV_INDEX=1
 	make test
 	;;
 linux-clang)
diff --git a/command-list.txt b/command-list.txt
index 9379b02..a289f09 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -204,6 +204,7 @@
 gitglossary                             guide
 githooks                                guide
 gitignore                               guide
+gitmailmap                              guide
 gitmodules                              guide
 gitnamespaces                           guide
 gitremote-helpers                       guide
diff --git a/commit-graph.c b/commit-graph.c
index 0316410..ca025ce 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -7,7 +7,7 @@
 #include "object.h"
 #include "refs.h"
 #include "revision.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "commit-graph.h"
 #include "object-store.h"
 #include "alloc.h"
@@ -19,6 +19,7 @@
 #include "shallow.h"
 #include "json-writer.h"
 #include "trace2.h"
+#include "chunk-format.h"
 
 void git_test_write_commit_graph_or_die(void)
 {
@@ -38,11 +39,12 @@
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
+#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
+#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f56 /* "GDOV" */
 #define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 #define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
 #define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
 #define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
-#define MAX_NUM_CHUNKS 7
 
 #define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
@@ -57,13 +59,16 @@
 
 #define GRAPH_HEADER_SIZE 8
 #define GRAPH_FANOUT_SIZE (4 * 256)
-#define GRAPH_CHUNKLOOKUP_WIDTH 12
-#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
+#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * CHUNK_TOC_ENTRY_SIZE \
 			+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
+#define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW (1ULL << 31)
+
 /* Remember to update object flag allocation in object.h */
 #define REACHABLE       (1u<<15)
 
+define_commit_slab(topo_level_slab, uint32_t);
+
 /* Keep track of the order in which commits are added to our list. */
 define_commit_slab(commit_pos, int);
 static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos);
@@ -99,7 +104,7 @@
 	return data ? data->graph_pos : COMMIT_NOT_FROM_GRAPH;
 }
 
-uint32_t commit_graph_generation(const struct commit *c)
+timestamp_t commit_graph_generation(const struct commit *c)
 {
 	struct commit_graph_data *data =
 		commit_graph_data_slab_peek(&commit_graph_data_slab, c);
@@ -139,13 +144,17 @@
 	return data;
 }
 
+/*
+ * Should be used only while writing commit-graph as it compares
+ * generation value of commits by directly accessing commit-slab.
+ */
 static int commit_gen_cmp(const void *va, const void *vb)
 {
 	const struct commit *a = *(const struct commit **)va;
 	const struct commit *b = *(const struct commit **)vb;
 
-	uint32_t generation_a = commit_graph_generation(a);
-	uint32_t generation_b = commit_graph_generation(b);
+	const timestamp_t generation_a = commit_graph_data_at(a)->generation;
+	const timestamp_t generation_b = commit_graph_data_at(b)->generation;
 	/* lower generation commits first */
 	if (generation_a < generation_b)
 		return -1;
@@ -288,15 +297,43 @@
 	return 0;
 }
 
+static int graph_read_oid_lookup(const unsigned char *chunk_start,
+				 size_t chunk_size, void *data)
+{
+	struct commit_graph *g = data;
+	g->chunk_oid_lookup = chunk_start;
+	g->num_commits = chunk_size / g->hash_len;
+	return 0;
+}
+
+static int graph_read_bloom_data(const unsigned char *chunk_start,
+				  size_t chunk_size, void *data)
+{
+	struct commit_graph *g = data;
+	uint32_t hash_version;
+	g->chunk_bloom_data = chunk_start;
+	hash_version = get_be32(chunk_start);
+
+	if (hash_version != 1)
+		return 0;
+
+	g->bloom_filter_settings = xmalloc(sizeof(struct bloom_filter_settings));
+	g->bloom_filter_settings->hash_version = hash_version;
+	g->bloom_filter_settings->num_hashes = get_be32(chunk_start + 4);
+	g->bloom_filter_settings->bits_per_entry = get_be32(chunk_start + 8);
+	g->bloom_filter_settings->max_changed_paths = DEFAULT_BLOOM_MAX_CHANGES;
+
+	return 0;
+}
+
 struct commit_graph *parse_commit_graph(struct repository *r,
 					void *graph_map, size_t graph_size)
 {
-	const unsigned char *data, *chunk_lookup;
-	uint32_t i;
+	const unsigned char *data;
 	struct commit_graph *graph;
-	uint64_t next_chunk_offset;
 	uint32_t graph_signature;
 	unsigned char graph_version, hash_version;
+	struct chunkfile *cf = NULL;
 
 	if (!graph_map)
 		return NULL;
@@ -337,7 +374,7 @@
 	graph->data_len = graph_size;
 
 	if (graph_size < GRAPH_HEADER_SIZE +
-			 (graph->num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH +
+			 (graph->num_chunks + 1) * CHUNK_TOC_ENTRY_SIZE +
 			 GRAPH_FANOUT_SIZE + the_hash_algo->rawsz) {
 		error(_("commit-graph file is too small to hold %u chunks"),
 		      graph->num_chunks);
@@ -345,94 +382,28 @@
 		return NULL;
 	}
 
-	chunk_lookup = data + 8;
-	next_chunk_offset = get_be64(chunk_lookup + 4);
-	for (i = 0; i < graph->num_chunks; i++) {
-		uint32_t chunk_id;
-		uint64_t chunk_offset = next_chunk_offset;
-		int chunk_repeated = 0;
+	cf = init_chunkfile(NULL);
 
-		chunk_id = get_be32(chunk_lookup + 0);
+	if (read_table_of_contents(cf, graph->data, graph_size,
+				   GRAPH_HEADER_SIZE, graph->num_chunks))
+		goto free_and_return;
 
-		chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
-		next_chunk_offset = get_be64(chunk_lookup + 4);
+	pair_chunk(cf, GRAPH_CHUNKID_OIDFANOUT,
+		   (const unsigned char **)&graph->chunk_oid_fanout);
+	read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph);
+	pair_chunk(cf, GRAPH_CHUNKID_DATA, &graph->chunk_commit_data);
+	pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges);
+	pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs);
+	pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
+		   &graph->chunk_generation_data);
+	pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
+		   &graph->chunk_generation_data_overflow);
 
-		if (chunk_offset > graph_size - the_hash_algo->rawsz) {
-			error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
-			      (uint32_t)chunk_offset);
-			goto free_and_return;
-		}
-
-		switch (chunk_id) {
-		case GRAPH_CHUNKID_OIDFANOUT:
-			if (graph->chunk_oid_fanout)
-				chunk_repeated = 1;
-			else
-				graph->chunk_oid_fanout = (uint32_t*)(data + chunk_offset);
-			break;
-
-		case GRAPH_CHUNKID_OIDLOOKUP:
-			if (graph->chunk_oid_lookup)
-				chunk_repeated = 1;
-			else {
-				graph->chunk_oid_lookup = data + chunk_offset;
-				graph->num_commits = (next_chunk_offset - chunk_offset)
-						     / graph->hash_len;
-			}
-			break;
-
-		case GRAPH_CHUNKID_DATA:
-			if (graph->chunk_commit_data)
-				chunk_repeated = 1;
-			else
-				graph->chunk_commit_data = data + chunk_offset;
-			break;
-
-		case GRAPH_CHUNKID_EXTRAEDGES:
-			if (graph->chunk_extra_edges)
-				chunk_repeated = 1;
-			else
-				graph->chunk_extra_edges = data + chunk_offset;
-			break;
-
-		case GRAPH_CHUNKID_BASE:
-			if (graph->chunk_base_graphs)
-				chunk_repeated = 1;
-			else
-				graph->chunk_base_graphs = data + chunk_offset;
-			break;
-
-		case GRAPH_CHUNKID_BLOOMINDEXES:
-			if (graph->chunk_bloom_indexes)
-				chunk_repeated = 1;
-			else if (r->settings.commit_graph_read_changed_paths)
-				graph->chunk_bloom_indexes = data + chunk_offset;
-			break;
-
-		case GRAPH_CHUNKID_BLOOMDATA:
-			if (graph->chunk_bloom_data)
-				chunk_repeated = 1;
-			else if (r->settings.commit_graph_read_changed_paths) {
-				uint32_t hash_version;
-				graph->chunk_bloom_data = data + chunk_offset;
-				hash_version = get_be32(data + chunk_offset);
-
-				if (hash_version != 1)
-					break;
-
-				graph->bloom_filter_settings = xmalloc(sizeof(struct bloom_filter_settings));
-				graph->bloom_filter_settings->hash_version = hash_version;
-				graph->bloom_filter_settings->num_hashes = get_be32(data + chunk_offset + 4);
-				graph->bloom_filter_settings->bits_per_entry = get_be32(data + chunk_offset + 8);
-				graph->bloom_filter_settings->max_changed_paths = DEFAULT_BLOOM_MAX_CHANGES;
-			}
-			break;
-		}
-
-		if (chunk_repeated) {
-			error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
-			goto free_and_return;
-		}
+	if (r->settings.commit_graph_read_changed_paths) {
+		pair_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
+			   &graph->chunk_bloom_indexes);
+		read_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
+			   graph_read_bloom_data, graph);
 	}
 
 	if (graph->chunk_bloom_indexes && graph->chunk_bloom_data) {
@@ -449,9 +420,11 @@
 	if (verify_commit_graph_lite(graph))
 		goto free_and_return;
 
+	free_chunkfile(cf);
 	return graph;
 
 free_and_return:
+	free_chunkfile(cf);
 	free(graph->bloom_filter_settings);
 	free(graph);
 	return NULL;
@@ -590,6 +563,31 @@
 	return graph_chain;
 }
 
+/*
+ * returns 1 if and only if all graphs in the chain have
+ * corrected commit dates stored in the generation_data chunk.
+ */
+static int validate_mixed_generation_chain(struct commit_graph *g)
+{
+	int read_generation_data = 1;
+	struct commit_graph *p = g;
+
+	while (read_generation_data && p) {
+		read_generation_data = p->read_generation_data;
+		p = p->base_graph;
+	}
+
+	if (read_generation_data)
+		return 1;
+
+	while (g) {
+		g->read_generation_data = 0;
+		g = g->base_graph;
+	}
+
+	return 0;
+}
+
 struct commit_graph *read_commit_graph_one(struct repository *r,
 					   struct object_directory *odb)
 {
@@ -598,6 +596,8 @@
 	if (!g)
 		g = load_commit_graph_chain(r, odb);
 
+	validate_mixed_generation_chain(g);
+
 	return g;
 }
 
@@ -673,6 +673,20 @@
 	return !!first_generation;
 }
 
+int corrected_commit_dates_enabled(struct repository *r)
+{
+	struct commit_graph *g;
+	if (!prepare_commit_graph(r))
+		return 0;
+
+	g = r->objects->commit_graph;
+
+	if (!g->num_commits)
+		return 0;
+
+	return g->read_generation_data;
+}
+
 struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 {
 	struct commit_graph *g = r->objects->commit_graph;
@@ -748,17 +762,41 @@
 {
 	const unsigned char *commit_data;
 	struct commit_graph_data *graph_data;
-	uint32_t lex_index;
+	uint32_t lex_index, offset_pos;
+	uint64_t date_high, date_low, offset;
 
 	while (pos < g->num_commits_in_base)
 		g = g->base_graph;
 
+	if (pos >= g->num_commits + g->num_commits_in_base)
+		die(_("invalid commit position. commit-graph is likely corrupt"));
+
 	lex_index = pos - g->num_commits_in_base;
 	commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
 
 	graph_data = commit_graph_data_at(item);
 	graph_data->graph_pos = pos;
-	graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
+	date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
+	date_low = get_be32(commit_data + g->hash_len + 12);
+	item->date = (timestamp_t)((date_high << 32) | date_low);
+
+	if (g->read_generation_data) {
+		offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+
+		if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
+			if (!g->chunk_generation_data_overflow)
+				die(_("commit-graph requires overflow generation data but has none"));
+
+			offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
+			graph_data->generation = get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
+		} else
+			graph_data->generation = item->date + offset;
+	} else
+		graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
+	if (g->topo_levels)
+		*topo_level_slab_at(g->topo_levels, item) = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
 static inline void set_commit_tree(struct commit *c, struct tree *t)
@@ -772,38 +810,22 @@
 {
 	uint32_t edge_value;
 	uint32_t *parent_data_ptr;
-	uint64_t date_low, date_high;
 	struct commit_list **pptr;
-	struct commit_graph_data *graph_data;
 	const unsigned char *commit_data;
 	uint32_t lex_index;
 
 	while (pos < g->num_commits_in_base)
 		g = g->base_graph;
 
-	if (pos >= g->num_commits + g->num_commits_in_base)
-		die(_("invalid commit position. commit-graph is likely corrupt"));
+	fill_commit_graph_info(item, g, pos);
 
-	/*
-	 * Store the "full" position, but then use the
-	 * "local" position for the rest of the calculation.
-	 */
-	graph_data = commit_graph_data_at(item);
-	graph_data->graph_pos = pos;
 	lex_index = pos - g->num_commits_in_base;
-
 	commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
 
 	item->object.parsed = 1;
 
 	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);
-	item->date = (timestamp_t)((date_high << 32) | date_low);
-
-	graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
-
 	pptr = &item->parents;
 
 	edge_value = get_be32(commit_data + g->hash_len);
@@ -943,6 +965,7 @@
 	struct oid_array oids;
 	struct packed_commit_list commits;
 	int num_extra_edges;
+	int num_generation_data_overflows;
 	unsigned long approx_nr_objects;
 	struct progress *progress;
 	int progress_done;
@@ -961,8 +984,11 @@
 		 report_progress:1,
 		 split:1,
 		 changed_paths:1,
-		 order_by_pack:1;
+		 order_by_pack:1,
+		 write_generation_data:1,
+		 trust_generation_numbers:1;
 
+	struct topo_level_slab *topo_levels;
 	const struct commit_graph_opts *opts;
 	size_t total_bloom_filter_data_size;
 	const struct bloom_filter_settings *bloom_settings;
@@ -974,8 +1000,9 @@
 };
 
 static int write_graph_chunk_fanout(struct hashfile *f,
-				    struct write_commit_graph_context *ctx)
+				    void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	int i, count = 0;
 	struct commit **list = ctx->commits.list;
 
@@ -1000,8 +1027,9 @@
 }
 
 static int write_graph_chunk_oids(struct hashfile *f,
-				  struct write_commit_graph_context *ctx)
+				  void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	struct commit **list = ctx->commits.list;
 	int count;
 	for (count = 0; count < ctx->commits.nr; count++, list++) {
@@ -1012,15 +1040,16 @@
 	return 0;
 }
 
-static const unsigned char *commit_to_sha1(size_t index, void *table)
+static const struct object_id *commit_to_oid(size_t index, const void *table)
 {
-	struct commit **commits = table;
-	return commits[index]->object.oid.hash;
+	const struct commit * const *commits = table;
+	return &commits[index]->object.oid;
 }
 
 static int write_graph_chunk_data(struct hashfile *f,
-				  struct write_commit_graph_context *ctx)
+				  void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	uint32_t num_extra_edges = 0;
@@ -1032,7 +1061,7 @@
 		uint32_t packedDate[2];
 		display_progress(ctx->progress, ++ctx->progress_cnt);
 
-		if (parse_commit_no_graph(*list))
+		if (repo_parse_commit_no_graph(ctx->r, *list))
 			die(_("unable to parse commit %s"),
 				oid_to_hex(&(*list)->object.oid));
 		tree = get_commit_tree_oid(*list);
@@ -1043,10 +1072,10 @@
 		if (!parent)
 			edge_value = GRAPH_PARENT_NONE;
 		else {
-			edge_value = sha1_pos(parent->item->object.oid.hash,
-					      ctx->commits.list,
-					      ctx->commits.nr,
-					      commit_to_sha1);
+			edge_value = oid_pos(&parent->item->object.oid,
+					     ctx->commits.list,
+					     ctx->commits.nr,
+					     commit_to_oid);
 
 			if (edge_value >= 0)
 				edge_value += ctx->new_num_commits_in_base;
@@ -1074,10 +1103,10 @@
 		else if (parent->next)
 			edge_value = GRAPH_EXTRA_EDGES_NEEDED | num_extra_edges;
 		else {
-			edge_value = sha1_pos(parent->item->object.oid.hash,
-					      ctx->commits.list,
-					      ctx->commits.nr,
-					      commit_to_sha1);
+			edge_value = oid_pos(&parent->item->object.oid,
+					     ctx->commits.list,
+					     ctx->commits.nr,
+					     commit_to_oid);
 
 			if (edge_value >= 0)
 				edge_value += ctx->new_num_commits_in_base;
@@ -1109,7 +1138,7 @@
 		else
 			packedDate[0] = 0;
 
-		packedDate[0] |= htonl(commit_graph_data_at(*list)->generation << 2);
+		packedDate[0] |= htonl(*topo_level_slab_at(ctx->topo_levels, *list) << 2);
 
 		packedDate[1] = htonl((*list)->date);
 		hashwrite(f, packedDate, 8);
@@ -1120,9 +1149,53 @@
 	return 0;
 }
 
-static int write_graph_chunk_extra_edges(struct hashfile *f,
-					 struct write_commit_graph_context *ctx)
+static int write_graph_chunk_generation_data(struct hashfile *f,
+					     void *data)
 {
+	struct write_commit_graph_context *ctx = data;
+	int i, num_generation_data_overflows = 0;
+
+	for (i = 0; i < ctx->commits.nr; i++) {
+		struct commit *c = ctx->commits.list[i];
+		timestamp_t offset;
+		repo_parse_commit(ctx->r, c);
+		offset = commit_graph_data_at(c)->generation - c->date;
+		display_progress(ctx->progress, ++ctx->progress_cnt);
+
+		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
+			offset = CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW | num_generation_data_overflows;
+			num_generation_data_overflows++;
+		}
+
+		hashwrite_be32(f, offset);
+	}
+
+	return 0;
+}
+
+static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
+						      void *data)
+{
+	struct write_commit_graph_context *ctx = data;
+	int i;
+	for (i = 0; i < ctx->commits.nr; i++) {
+		struct commit *c = ctx->commits.list[i];
+		timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+		display_progress(ctx->progress, ++ctx->progress_cnt);
+
+		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
+			hashwrite_be32(f, offset >> 32);
+			hashwrite_be32(f, (uint32_t) offset);
+		}
+	}
+
+	return 0;
+}
+
+static int write_graph_chunk_extra_edges(struct hashfile *f,
+					 void *data)
+{
+	struct write_commit_graph_context *ctx = data;
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	struct commit_list *parent;
@@ -1143,10 +1216,10 @@
 
 		/* Since num_parents > 2, this initializer is safe. */
 		for (parent = (*list)->parents->next; parent; parent = parent->next) {
-			int edge_value = sha1_pos(parent->item->object.oid.hash,
-						  ctx->commits.list,
-						  ctx->commits.nr,
-						  commit_to_sha1);
+			int edge_value = oid_pos(&parent->item->object.oid,
+						 ctx->commits.list,
+						 ctx->commits.nr,
+						 commit_to_oid);
 
 			if (edge_value >= 0)
 				edge_value += ctx->new_num_commits_in_base;
@@ -1175,8 +1248,9 @@
 }
 
 static int write_graph_chunk_bloom_indexes(struct hashfile *f,
-					   struct write_commit_graph_context *ctx)
+					   void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
 	uint32_t cur_pos = 0;
@@ -1210,8 +1284,9 @@
 }
 
 static int write_graph_chunk_bloom_data(struct hashfile *f,
-					struct write_commit_graph_context *ctx)
+					void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	struct commit **list = ctx->commits.list;
 	struct commit **last = ctx->commits.list + ctx->commits.nr;
 
@@ -1306,11 +1381,11 @@
 		if (!commit)
 			continue;
 		if (ctx->split) {
-			if ((!parse_commit(commit) &&
+			if ((!repo_parse_commit(ctx->r, commit) &&
 			     commit_graph_position(commit) == COMMIT_NOT_FROM_GRAPH) ||
 			    flags == COMMIT_GRAPH_SPLIT_REPLACE)
 				add_missing_parents(ctx, commit);
-		} else if (!parse_commit_no_graph(commit))
+		} else if (!repo_parse_commit_no_graph(ctx->r, commit))
 			add_missing_parents(ctx, commit);
 	}
 	stop_progress(&ctx->progress);
@@ -1329,6 +1404,59 @@
 	stop_progress(&ctx->progress);
 }
 
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+	int i;
+	struct commit_list *list = NULL;
+
+	if (ctx->report_progress)
+		ctx->progress = start_delayed_progress(
+					_("Computing commit graph topological levels"),
+					ctx->commits.nr);
+	for (i = 0; i < ctx->commits.nr; i++) {
+		struct commit *c = ctx->commits.list[i];
+		uint32_t level;
+
+		repo_parse_commit(ctx->r, c);
+		level = *topo_level_slab_at(ctx->topo_levels, c);
+
+		display_progress(ctx->progress, i + 1);
+		if (level != GENERATION_NUMBER_ZERO)
+			continue;
+
+		commit_list_insert(c, &list);
+		while (list) {
+			struct commit *current = list->item;
+			struct commit_list *parent;
+			int all_parents_computed = 1;
+			uint32_t max_level = 0;
+
+			for (parent = current->parents; parent; parent = parent->next) {
+				repo_parse_commit(ctx->r, parent->item);
+				level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+
+				if (level == GENERATION_NUMBER_ZERO) {
+					all_parents_computed = 0;
+					commit_list_insert(parent->item, &list);
+					break;
+				}
+
+				if (level > max_level)
+					max_level = level;
+			}
+
+			if (all_parents_computed) {
+				pop_commit(&list);
+
+				if (max_level > GENERATION_NUMBER_V1_MAX - 1)
+					max_level = GENERATION_NUMBER_V1_MAX - 1;
+				*topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+			}
+		}
+	}
+	stop_progress(&ctx->progress);
+}
+
 static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
 	int i;
@@ -1338,42 +1466,56 @@
 		ctx->progress = start_delayed_progress(
 					_("Computing commit graph generation numbers"),
 					ctx->commits.nr);
+
+	if (!ctx->trust_generation_numbers) {
+		for (i = 0; i < ctx->commits.nr; i++) {
+			struct commit *c = ctx->commits.list[i];
+			repo_parse_commit(ctx->r, c);
+			commit_graph_data_at(c)->generation = GENERATION_NUMBER_ZERO;
+		}
+	}
+
 	for (i = 0; i < ctx->commits.nr; i++) {
-		uint32_t generation = commit_graph_data_at(ctx->commits.list[i])->generation;
+		struct commit *c = ctx->commits.list[i];
+		timestamp_t corrected_commit_date;
+
+		repo_parse_commit(ctx->r, c);
+		corrected_commit_date = commit_graph_data_at(c)->generation;
 
 		display_progress(ctx->progress, i + 1);
-		if (generation != GENERATION_NUMBER_INFINITY &&
-		    generation != GENERATION_NUMBER_ZERO)
+		if (corrected_commit_date != GENERATION_NUMBER_ZERO)
 			continue;
 
-		commit_list_insert(ctx->commits.list[i], &list);
+		commit_list_insert(c, &list);
 		while (list) {
 			struct commit *current = list->item;
 			struct commit_list *parent;
 			int all_parents_computed = 1;
-			uint32_t max_generation = 0;
+			timestamp_t max_corrected_commit_date = 0;
 
 			for (parent = current->parents; parent; parent = parent->next) {
-				generation = commit_graph_data_at(parent->item)->generation;
+				repo_parse_commit(ctx->r, parent->item);
+				corrected_commit_date = commit_graph_data_at(parent->item)->generation;
 
-				if (generation == GENERATION_NUMBER_INFINITY ||
-				    generation == GENERATION_NUMBER_ZERO) {
+				if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
 					all_parents_computed = 0;
 					commit_list_insert(parent->item, &list);
 					break;
-				} else if (generation > max_generation) {
-					max_generation = generation;
 				}
+
+				if (corrected_commit_date > max_corrected_commit_date)
+					max_corrected_commit_date = corrected_commit_date;
 			}
 
 			if (all_parents_computed) {
-				struct commit_graph_data *data = commit_graph_data_at(current);
-
-				data->generation = max_generation + 1;
 				pop_commit(&list);
 
-				if (data->generation > GENERATION_NUMBER_MAX)
-					data->generation = GENERATION_NUMBER_MAX;
+				if (current->date && current->date > max_corrected_commit_date)
+					max_corrected_commit_date = current->date - 1;
+				commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
+
+				if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
+					ctx->num_generation_data_overflows++;
 			}
 		}
 	}
@@ -1458,7 +1600,7 @@
 	struct object_id peeled;
 	struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
 
-	if (!peel_ref(refname, &peeled))
+	if (!peel_iterated_oid(oid, &peeled))
 		oid = &peeled;
 	if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
 		oidset_insert(data->commits, oid);
@@ -1593,9 +1735,9 @@
 			continue;
 
 		if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
-			parse_commit(ctx->commits.list[ctx->commits.nr]);
+			repo_parse_commit(ctx->r, ctx->commits.list[ctx->commits.nr]);
 		else
-			parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
+			repo_parse_commit_no_graph(ctx->r, ctx->commits.list[ctx->commits.nr]);
 
 		num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
 		if (num_parents > 2)
@@ -1620,8 +1762,9 @@
 }
 
 static int write_graph_chunk_base(struct hashfile *f,
-				  struct write_commit_graph_context *ctx)
+				    void *data)
 {
+	struct write_commit_graph_context *ctx = data;
 	int num = write_graph_chunk_base_1(f, ctx->new_base_graph);
 
 	if (num != ctx->num_commit_graphs_after - 1) {
@@ -1632,27 +1775,16 @@
 	return 0;
 }
 
-typedef int (*chunk_write_fn)(struct hashfile *f,
-			      struct write_commit_graph_context *ctx);
-
-struct chunk_info {
-	uint32_t id;
-	uint64_t size;
-	chunk_write_fn write_fn;
-};
-
 static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 {
 	uint32_t i;
 	int fd;
 	struct hashfile *f;
 	struct lock_file lk = LOCK_INIT;
-	struct chunk_info chunks[MAX_NUM_CHUNKS + 1];
 	const unsigned hashsz = the_hash_algo->rawsz;
 	struct strbuf progress_title = STRBUF_INIT;
-	int num_chunks = 3;
-	uint64_t chunk_offset;
 	struct object_id file_hash;
+	struct chunkfile *cf;
 
 	if (ctx->split) {
 		struct strbuf tmp_file = STRBUF_INIT;
@@ -1698,83 +1830,62 @@
 		f = hashfd(fd, get_lock_file_path(&lk));
 	}
 
-	chunks[0].id = GRAPH_CHUNKID_OIDFANOUT;
-	chunks[0].size = GRAPH_FANOUT_SIZE;
-	chunks[0].write_fn = write_graph_chunk_fanout;
-	chunks[1].id = GRAPH_CHUNKID_OIDLOOKUP;
-	chunks[1].size = hashsz * ctx->commits.nr;
-	chunks[1].write_fn = write_graph_chunk_oids;
-	chunks[2].id = GRAPH_CHUNKID_DATA;
-	chunks[2].size = (hashsz + 16) * ctx->commits.nr;
-	chunks[2].write_fn = write_graph_chunk_data;
-	if (ctx->num_extra_edges) {
-		chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
-		chunks[num_chunks].size = 4 * ctx->num_extra_edges;
-		chunks[num_chunks].write_fn = write_graph_chunk_extra_edges;
-		num_chunks++;
-	}
-	if (ctx->changed_paths) {
-		chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMINDEXES;
-		chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
-		chunks[num_chunks].write_fn = write_graph_chunk_bloom_indexes;
-		num_chunks++;
-		chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMDATA;
-		chunks[num_chunks].size = sizeof(uint32_t) * 3
-					  + ctx->total_bloom_filter_data_size;
-		chunks[num_chunks].write_fn = write_graph_chunk_bloom_data;
-		num_chunks++;
-	}
-	if (ctx->num_commit_graphs_after > 1) {
-		chunks[num_chunks].id = GRAPH_CHUNKID_BASE;
-		chunks[num_chunks].size = hashsz * (ctx->num_commit_graphs_after - 1);
-		chunks[num_chunks].write_fn = write_graph_chunk_base;
-		num_chunks++;
-	}
+	cf = init_chunkfile(f);
 
-	chunks[num_chunks].id = 0;
-	chunks[num_chunks].size = 0;
+	add_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, GRAPH_FANOUT_SIZE,
+		  write_graph_chunk_fanout);
+	add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, hashsz * ctx->commits.nr,
+		  write_graph_chunk_oids);
+	add_chunk(cf, GRAPH_CHUNKID_DATA, (hashsz + 16) * ctx->commits.nr,
+		  write_graph_chunk_data);
+
+	if (git_env_bool(GIT_TEST_COMMIT_GRAPH_NO_GDAT, 0))
+		ctx->write_generation_data = 0;
+	if (ctx->write_generation_data)
+		add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA,
+			  sizeof(uint32_t) * ctx->commits.nr,
+			  write_graph_chunk_generation_data);
+	if (ctx->num_generation_data_overflows)
+		add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
+			  sizeof(timestamp_t) * ctx->num_generation_data_overflows,
+			  write_graph_chunk_generation_data_overflow);
+	if (ctx->num_extra_edges)
+		add_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES,
+			  4 * ctx->num_extra_edges,
+			  write_graph_chunk_extra_edges);
+	if (ctx->changed_paths) {
+		add_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES,
+			  sizeof(uint32_t) * ctx->commits.nr,
+			  write_graph_chunk_bloom_indexes);
+		add_chunk(cf, GRAPH_CHUNKID_BLOOMDATA,
+			  sizeof(uint32_t) * 3
+				+ ctx->total_bloom_filter_data_size,
+			  write_graph_chunk_bloom_data);
+	}
+	if (ctx->num_commit_graphs_after > 1)
+		add_chunk(cf, GRAPH_CHUNKID_BASE,
+			  hashsz * (ctx->num_commit_graphs_after - 1),
+			  write_graph_chunk_base);
 
 	hashwrite_be32(f, GRAPH_SIGNATURE);
 
 	hashwrite_u8(f, GRAPH_VERSION);
 	hashwrite_u8(f, oid_version());
-	hashwrite_u8(f, num_chunks);
+	hashwrite_u8(f, get_num_chunks(cf));
 	hashwrite_u8(f, ctx->num_commit_graphs_after - 1);
 
-	chunk_offset = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
-	for (i = 0; i <= num_chunks; i++) {
-		uint32_t chunk_write[3];
-
-		chunk_write[0] = htonl(chunks[i].id);
-		chunk_write[1] = htonl(chunk_offset >> 32);
-		chunk_write[2] = htonl(chunk_offset & 0xffffffff);
-		hashwrite(f, chunk_write, 12);
-
-		chunk_offset += chunks[i].size;
-	}
-
 	if (ctx->report_progress) {
 		strbuf_addf(&progress_title,
 			    Q_("Writing out commit graph in %d pass",
 			       "Writing out commit graph in %d passes",
-			       num_chunks),
-			    num_chunks);
+			       get_num_chunks(cf)),
+			    get_num_chunks(cf));
 		ctx->progress = start_delayed_progress(
 			progress_title.buf,
-			num_chunks * ctx->commits.nr);
+			get_num_chunks(cf) * ctx->commits.nr);
 	}
 
-	for (i = 0; i < num_chunks; i++) {
-		uint64_t start_offset = f->total + f->offset;
-
-		if (chunks[i].write_fn(f, ctx))
-			return -1;
-
-		if (f->total + f->offset != start_offset + chunks[i].size)
-			BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
-			    chunks[i].size, chunks[i].id,
-			    f->total + f->offset - start_offset);
-	}
+	write_chunkfile(cf, ctx);
 
 	stop_progress(&ctx->progress);
 	strbuf_release(&progress_title);
@@ -1791,6 +1902,7 @@
 
 	close_commit_graph(ctx->r->objects);
 	finalize_hashfile(f, file_hash.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
+	free_chunkfile(cf);
 
 	if (ctx->split) {
 		FILE *chainf = fdopen_lock_file(&lk, "w");
@@ -1918,6 +2030,13 @@
 		if (i < ctx->num_commit_graphs_after)
 			ctx->commit_graph_hash_after[i] = xstrdup(oid_to_hex(&g->oid));
 
+		/*
+		 * If the topmost remaining layer has generation data chunk, the
+		 * resultant layer also has generation data chunk.
+		 */
+		if (i == ctx->num_commit_graphs_after - 2)
+			ctx->write_generation_data = !!g->chunk_generation_data;
+
 		i--;
 		g = g->base_graph;
 	}
@@ -2109,6 +2228,7 @@
 	int res = 0;
 	int replace = 0;
 	struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+	struct topo_level_slab topo_levels;
 
 	prepare_repo_settings(the_repository);
 	if (!the_repository->settings.core_commit_graph) {
@@ -2126,6 +2246,8 @@
 	ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
 	ctx->opts = opts;
 	ctx->total_bloom_filter_data_size = 0;
+	ctx->write_generation_data = 1;
+	ctx->num_generation_data_overflows = 0;
 
 	bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
 						      bloom_settings.bits_per_entry);
@@ -2135,11 +2257,23 @@
 							 bloom_settings.max_changed_paths);
 	ctx->bloom_settings = &bloom_settings;
 
+	init_topo_level_slab(&topo_levels);
+	ctx->topo_levels = &topo_levels;
+
+	prepare_commit_graph(ctx->r);
+	if (ctx->r->objects->commit_graph) {
+		struct commit_graph *g = ctx->r->objects->commit_graph;
+
+		while (g) {
+			g->topo_levels = &topo_levels;
+			g = g->base_graph;
+		}
+	}
+
 	if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
 		ctx->changed_paths = 1;
 	if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
 		struct commit_graph *g;
-		prepare_commit_graph_one(ctx->r, ctx->odb);
 
 		g = ctx->r->objects->commit_graph;
 
@@ -2151,10 +2285,7 @@
 	}
 
 	if (ctx->split) {
-		struct commit_graph *g;
-		prepare_commit_graph(ctx->r);
-
-		g = ctx->r->objects->commit_graph;
+		struct commit_graph *g = ctx->r->objects->commit_graph;
 
 		while (g) {
 			ctx->num_commit_graphs_before++;
@@ -2178,9 +2309,6 @@
 
 	ctx->approx_nr_objects = approximate_object_count();
 
-	if (ctx->append)
-		prepare_commit_graph_one(ctx->r, ctx->odb);
-
 	if (ctx->append && ctx->r->objects->commit_graph) {
 		struct commit_graph *g = ctx->r->objects->commit_graph;
 		for (i = 0; i < g->num_commits; i++) {
@@ -2227,7 +2355,11 @@
 	} else
 		ctx->num_commit_graphs_after = 1;
 
-	compute_generation_numbers(ctx);
+	ctx->trust_generation_numbers = validate_mixed_generation_chain(ctx->r->objects->commit_graph);
+
+	compute_topological_levels(ctx);
+	if (ctx->write_generation_data)
+		compute_generation_numbers(ctx);
 
 	if (ctx->changed_paths)
 		compute_bloom_filters(ctx);
@@ -2243,6 +2375,7 @@
 	free(ctx->graph_name);
 	free(ctx->commits.list);
 	oid_array_clear(&ctx->oids);
+	clear_topo_level_slab(&topo_levels);
 
 	if (ctx->commit_graph_filenames_after) {
 		for (i = 0; i < ctx->num_commit_graphs_after; i++) {
@@ -2355,8 +2488,8 @@
 	for (i = 0; i < g->num_commits; i++) {
 		struct commit *graph_commit, *odb_commit;
 		struct commit_list *graph_parents, *odb_parents;
-		uint32_t max_generation = 0;
-		uint32_t generation;
+		timestamp_t max_generation = 0;
+		timestamp_t generation;
 
 		display_progress(progress, i + 1);
 		hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
@@ -2420,16 +2553,17 @@
 			continue;
 
 		/*
-		 * If one of our parents has generation GENERATION_NUMBER_MAX, then
-		 * our generation is also GENERATION_NUMBER_MAX. Decrement to avoid
-		 * extra logic in the following condition.
+		 * If we are using topological level and one of our parents has
+		 * generation GENERATION_NUMBER_V1_MAX, then our generation is
+		 * also GENERATION_NUMBER_V1_MAX. Decrement to avoid extra logic
+		 * in the following condition.
 		 */
-		if (max_generation == GENERATION_NUMBER_MAX)
+		if (!g->read_generation_data && max_generation == GENERATION_NUMBER_V1_MAX)
 			max_generation--;
 
 		generation = commit_graph_generation(graph_commit);
-		if (generation != max_generation + 1)
-			graph_report(_("commit-graph generation for commit %s is %u != %u"),
+		if (generation < max_generation + 1)
+			graph_report(_("commit-graph generation for commit %s is %"PRItime" < %"PRItime),
 				     oid_to_hex(&cur_oid),
 				     generation,
 				     max_generation + 1);
diff --git a/commit-graph.h b/commit-graph.h
index f8e9250..97f3497 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -6,6 +6,7 @@
 #include "oidset.h"
 
 #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
+#define GIT_TEST_COMMIT_GRAPH_NO_GDAT "GIT_TEST_COMMIT_GRAPH_NO_GDAT"
 #define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
 #define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
 
@@ -63,16 +64,20 @@
 	struct object_directory *odb;
 
 	uint32_t num_commits_in_base;
+	unsigned int read_generation_data;
 	struct commit_graph *base_graph;
 
 	const uint32_t *chunk_oid_fanout;
 	const unsigned char *chunk_oid_lookup;
 	const unsigned char *chunk_commit_data;
+	const unsigned char *chunk_generation_data;
+	const unsigned char *chunk_generation_data_overflow;
 	const unsigned char *chunk_extra_edges;
 	const unsigned char *chunk_base_graphs;
 	const unsigned char *chunk_bloom_indexes;
 	const unsigned char *chunk_bloom_data;
 
+	struct topo_level_slab *topo_levels;
 	struct bloom_filter_settings *bloom_filter_settings;
 };
 
@@ -90,6 +95,12 @@
  */
 int generation_numbers_enabled(struct repository *r);
 
+/*
+ * Return 1 if and only if the repository has a commit-graph
+ * file and generation data chunk has been written for the file.
+ */
+int corrected_commit_dates_enabled(struct repository *r);
+
 struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
 
 enum commit_graph_write_flags {
@@ -144,12 +155,12 @@
 
 struct commit_graph_data {
 	uint32_t graph_pos;
-	uint32_t generation;
+	timestamp_t generation;
 };
 
 /*
  * Commits should be parsed before accessing generation, graph positions.
  */
-uint32_t commit_graph_generation(const struct commit *);
+timestamp_t commit_graph_generation(const struct commit *);
 uint32_t commit_graph_position(const struct commit *);
 #endif
diff --git a/commit-reach.c b/commit-reach.c
index 50175b1..2ea84d3 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -17,6 +17,25 @@
 
 static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
 
+static int compare_commits_by_gen(const void *_a, const void *_b)
+{
+	const struct commit *a = *(const struct commit * const *)_a;
+	const struct commit *b = *(const struct commit * const *)_b;
+
+	timestamp_t generation_a = commit_graph_generation(a);
+	timestamp_t generation_b = commit_graph_generation(b);
+
+	if (generation_a < generation_b)
+		return -1;
+	if (generation_a > generation_b)
+		return 1;
+	if (a->date < b->date)
+		return -1;
+	if (a->date > b->date)
+		return 1;
+	return 0;
+}
+
 static int queue_has_nonstale(struct prio_queue *queue)
 {
 	int i;
@@ -32,14 +51,14 @@
 static struct commit_list *paint_down_to_common(struct repository *r,
 						struct commit *one, int n,
 						struct commit **twos,
-						int min_generation)
+						timestamp_t min_generation)
 {
 	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
 	struct commit_list *result = NULL;
 	int i;
-	uint32_t last_gen = GENERATION_NUMBER_INFINITY;
+	timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 
-	if (!min_generation)
+	if (!min_generation && !corrected_commit_dates_enabled(r))
 		queue.compare = compare_commits_by_commit_date;
 
 	one->object.flags |= PARENT1;
@@ -58,10 +77,10 @@
 		struct commit *commit = prio_queue_get(&queue);
 		struct commit_list *parents;
 		int flags;
-		uint32_t generation = commit_graph_generation(commit);
+		timestamp_t generation = commit_graph_generation(commit);
 
 		if (min_generation && generation > last_gen)
-			BUG("bad generation skip %8x > %8x at %s",
+			BUG("bad generation skip %"PRItime" > %"PRItime" at %s",
 			    generation, last_gen,
 			    oid_to_hex(&commit->object.oid));
 		last_gen = generation;
@@ -156,14 +175,9 @@
 	return ret;
 }
 
-static int remove_redundant(struct repository *r, struct commit **array, int cnt)
+static int remove_redundant_no_gen(struct repository *r,
+				   struct commit **array, int cnt)
 {
-	/*
-	 * Some commit in the array may be an ancestor of
-	 * another commit.  Move such commit to the end of
-	 * the array, and return the number of commits that
-	 * are independent from each other.
-	 */
 	struct commit **work;
 	unsigned char *redundant;
 	int *filled_index;
@@ -177,12 +191,12 @@
 		repo_parse_commit(r, array[i]);
 	for (i = 0; i < cnt; i++) {
 		struct commit_list *common;
-		uint32_t min_generation = commit_graph_generation(array[i]);
+		timestamp_t min_generation = commit_graph_generation(array[i]);
 
 		if (redundant[i])
 			continue;
 		for (j = filled = 0; j < cnt; j++) {
-			uint32_t curr_generation;
+			timestamp_t curr_generation;
 			if (i == j || redundant[j])
 				continue;
 			filled_index[filled] = j;
@@ -209,15 +223,156 @@
 	for (i = filled = 0; i < cnt; i++)
 		if (!redundant[i])
 			array[filled++] = work[i];
-	for (j = filled, i = 0; i < cnt; i++)
-		if (redundant[i])
-			array[j++] = work[i];
 	free(work);
 	free(redundant);
 	free(filled_index);
 	return filled;
 }
 
+static int remove_redundant_with_gen(struct repository *r,
+				     struct commit **array, int cnt)
+{
+	int i, count_non_stale = 0, count_still_independent = cnt;
+	timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
+	struct commit **walk_start, **sorted;
+	size_t walk_start_nr = 0, walk_start_alloc = cnt;
+	int min_gen_pos = 0;
+
+	/*
+	 * Sort the input by generation number, ascending. This allows
+	 * us to increase the "min_generation" limit when we discover
+	 * the commit with lowest generation is STALE. The index
+	 * min_gen_pos points to the current position within 'array'
+	 * that is not yet known to be STALE.
+	 */
+	ALLOC_ARRAY(sorted, cnt);
+	COPY_ARRAY(sorted, array, cnt);
+	QSORT(sorted, cnt, compare_commits_by_gen);
+	min_generation = commit_graph_generation(sorted[0]);
+
+	ALLOC_ARRAY(walk_start, walk_start_alloc);
+
+	/* Mark all parents of the input as STALE */
+	for (i = 0; i < cnt; i++) {
+		struct commit_list *parents;
+
+		repo_parse_commit(r, array[i]);
+		array[i]->object.flags |= RESULT;
+		parents = array[i]->parents;
+
+		while (parents) {
+			repo_parse_commit(r, parents->item);
+			if (!(parents->item->object.flags & STALE)) {
+				parents->item->object.flags |= STALE;
+				ALLOC_GROW(walk_start, walk_start_nr + 1, walk_start_alloc);
+				walk_start[walk_start_nr++] = parents->item;
+			}
+			parents = parents->next;
+		}
+	}
+
+	QSORT(walk_start, walk_start_nr, compare_commits_by_gen);
+
+	/* remove STALE bit for now to allow walking through parents */
+	for (i = 0; i < walk_start_nr; i++)
+		walk_start[i]->object.flags &= ~STALE;
+
+	/*
+	 * Start walking from the highest generation. Hopefully, it will
+	 * find all other items during the first-parent walk, and we can
+	 * terminate early. Otherwise, we will do the same amount of work
+	 * as before.
+	 */
+	for (i = walk_start_nr - 1; i >= 0 && count_still_independent > 1; i--) {
+		/* push the STALE bits up to min generation */
+		struct commit_list *stack = NULL;
+
+		commit_list_insert(walk_start[i], &stack);
+		walk_start[i]->object.flags |= STALE;
+
+		while (stack) {
+			struct commit_list *parents;
+			struct commit *c = stack->item;
+
+			repo_parse_commit(r, c);
+
+			if (c->object.flags & RESULT) {
+				c->object.flags &= ~RESULT;
+				if (--count_still_independent <= 1)
+					break;
+				if (oideq(&c->object.oid, &sorted[min_gen_pos]->object.oid)) {
+					while (min_gen_pos < cnt - 1 &&
+					       (sorted[min_gen_pos]->object.flags & STALE))
+						min_gen_pos++;
+					min_generation = commit_graph_generation(sorted[min_gen_pos]);
+				}
+			}
+
+			if (commit_graph_generation(c) < min_generation) {
+				pop_commit(&stack);
+				continue;
+			}
+
+			parents = c->parents;
+			while (parents) {
+				if (!(parents->item->object.flags & STALE)) {
+					parents->item->object.flags |= STALE;
+					commit_list_insert(parents->item, &stack);
+					break;
+				}
+				parents = parents->next;
+			}
+
+			/* pop if all parents have been visited already */
+			if (!parents)
+				pop_commit(&stack);
+		}
+		free_commit_list(stack);
+	}
+	free(sorted);
+
+	/* clear result */
+	for (i = 0; i < cnt; i++)
+		array[i]->object.flags &= ~RESULT;
+
+	/* rearrange array */
+	for (i = count_non_stale = 0; i < cnt; i++) {
+		if (!(array[i]->object.flags & STALE))
+			array[count_non_stale++] = array[i];
+	}
+
+	/* clear marks */
+	clear_commit_marks_many(walk_start_nr, walk_start, STALE);
+	free(walk_start);
+
+	return count_non_stale;
+}
+
+static int remove_redundant(struct repository *r, struct commit **array, int cnt)
+{
+	/*
+	 * Some commit in the array may be an ancestor of
+	 * another commit.  Move the independent commits to the
+	 * beginning of 'array' and return their number. Callers
+	 * should not rely upon the contents of 'array' after
+	 * that number.
+	 */
+	if (generation_numbers_enabled(r)) {
+		int i;
+
+		/*
+		 * If we have a single commit with finite generation
+		 * number, then the _with_gen algorithm is preferred.
+		 */
+		for (i = 0; i < cnt; i++) {
+			if (commit_graph_generation(array[i]) < GENERATION_NUMBER_INFINITY)
+				return remove_redundant_with_gen(r, array, cnt);
+		}
+	}
+
+	return remove_redundant_no_gen(r, array, cnt);
+}
+
 static struct commit_list *get_merge_bases_many_0(struct repository *r,
 						  struct commit *one,
 						  int n,
@@ -321,7 +476,7 @@
 {
 	struct commit_list *bases;
 	int ret = 0, i;
-	uint32_t generation, max_generation = GENERATION_NUMBER_ZERO;
+	timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
 
 	if (repo_parse_commit(r, commit))
 		return ret;
@@ -470,7 +625,7 @@
 static enum contains_result contains_test(struct commit *candidate,
 					  const struct commit_list *want,
 					  struct contains_cache *cache,
-					  uint32_t cutoff)
+					  timestamp_t cutoff)
 {
 	enum contains_result *cached = contains_cache_at(cache, candidate);
 
@@ -506,11 +661,11 @@
 {
 	struct contains_stack contains_stack = { 0, 0, NULL };
 	enum contains_result result;
-	uint32_t cutoff = GENERATION_NUMBER_INFINITY;
+	timestamp_t cutoff = GENERATION_NUMBER_INFINITY;
 	const struct commit_list *p;
 
 	for (p = want; p; p = p->next) {
-		uint32_t generation;
+		timestamp_t generation;
 		struct commit *c = p->item;
 		load_commit_graph_info(the_repository, c);
 		generation = commit_graph_generation(c);
@@ -561,26 +716,11 @@
 	return repo_is_descendant_of(the_repository, commit, list);
 }
 
-static int compare_commits_by_gen(const void *_a, const void *_b)
-{
-	const struct commit *a = *(const struct commit * const *)_a;
-	const struct commit *b = *(const struct commit * const *)_b;
-
-	uint32_t generation_a = commit_graph_generation(a);
-	uint32_t generation_b = commit_graph_generation(b);
-
-	if (generation_a < generation_b)
-		return -1;
-	if (generation_a > generation_b)
-		return 1;
-	return 0;
-}
-
 int can_all_from_reach_with_flag(struct object_array *from,
 				 unsigned int with_flag,
 				 unsigned int assign_flag,
 				 time_t min_commit_date,
-				 uint32_t min_generation)
+				 timestamp_t min_generation)
 {
 	struct commit **list = NULL;
 	int i;
@@ -681,13 +821,13 @@
 	time_t min_commit_date = cutoff_by_min_date ? from->item->date : 0;
 	struct commit_list *from_iter = from, *to_iter = to;
 	int result;
-	uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+	timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
 
 	while (from_iter) {
 		add_object_array(&from_iter->item->object, NULL, &from_objs);
 
 		if (!parse_commit(from_iter->item)) {
-			uint32_t generation;
+			timestamp_t generation;
 			if (from_iter->item->date < min_commit_date)
 				min_commit_date = from_iter->item->date;
 
@@ -701,7 +841,7 @@
 
 	while (to_iter) {
 		if (!parse_commit(to_iter->item)) {
-			uint32_t generation;
+			timestamp_t generation;
 			if (to_iter->item->date < min_commit_date)
 				min_commit_date = to_iter->item->date;
 
@@ -741,13 +881,13 @@
 	struct commit_list *found_commits = NULL;
 	struct commit **to_last = to + nr_to;
 	struct commit **from_last = from + nr_from;
-	uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+	timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
 	int num_to_find = 0;
 
 	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
 
 	for (item = to; item < to_last; item++) {
-		uint32_t generation;
+		timestamp_t generation;
 		struct commit *c = *item;
 
 		parse_commit(c);
diff --git a/commit-reach.h b/commit-reach.h
index b49ad71..148b56f 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -87,7 +87,7 @@
 				 unsigned int with_flag,
 				 unsigned int assign_flag,
 				 time_t min_commit_date,
-				 uint32_t min_generation);
+				 timestamp_t min_generation);
 int can_all_from_reach(struct commit_list *from, struct commit_list *to,
 		       int commit_date_cutoff);
 
diff --git a/commit.c b/commit.c
index fe1fa3d..6ccd774 100644
--- a/commit.c
+++ b/commit.c
@@ -14,7 +14,7 @@
 #include "mergesort.h"
 #include "commit-slab.h"
 #include "prio-queue.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
 #include "refs.h"
@@ -105,23 +105,23 @@
 	return parse_timestamp(dateptr, NULL, 10);
 }
 
-static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
+static const struct object_id *commit_graft_oid_access(size_t index, const void *table)
 {
-	struct commit_graft **commit_graft_table = table;
-	return commit_graft_table[index]->oid.hash;
+	const struct commit_graft * const *commit_graft_table = table;
+	return &commit_graft_table[index]->oid;
 }
 
-int commit_graft_pos(struct repository *r, const unsigned char *sha1)
+int commit_graft_pos(struct repository *r, const struct object_id *oid)
 {
-	return sha1_pos(sha1, r->parsed_objects->grafts,
-			r->parsed_objects->grafts_nr,
-			commit_graft_sha1_access);
+	return oid_pos(oid, r->parsed_objects->grafts,
+		       r->parsed_objects->grafts_nr,
+		       commit_graft_oid_access);
 }
 
 int register_commit_graft(struct repository *r, struct commit_graft *graft,
 			  int ignore_dups)
 {
-	int pos = commit_graft_pos(r, graft->oid.hash);
+	int pos = commit_graft_pos(r, &graft->oid);
 
 	if (0 <= pos) {
 		if (ignore_dups)
@@ -232,7 +232,7 @@
 {
 	int pos;
 	prepare_commit_graft(r);
-	pos = commit_graft_pos(r, oid->hash);
+	pos = commit_graft_pos(r, oid);
 	if (pos < 0)
 		return NULL;
 	return r->parsed_objects->grafts[pos];
@@ -544,6 +544,17 @@
 	return new_list;
 }
 
+int commit_list_contains(struct commit *item, struct commit_list *list)
+{
+	while (list) {
+		if (list->item == item)
+			return 1;
+		list = list->next;
+	}
+
+	return 0;
+}
+
 unsigned commit_list_count(const struct commit_list *l)
 {
 	unsigned c = 0;
@@ -563,6 +574,17 @@
 	return head;
 }
 
+struct commit_list *reverse_commit_list(struct commit_list *list)
+{
+	struct commit_list *next = NULL, *current, *backup;
+	for (current = list; current; current = backup) {
+		backup = current->next;
+		current->next = next;
+		next = current;
+	}
+	return next;
+}
+
 void free_commit_list(struct commit_list *list)
 {
 	while (list)
@@ -731,8 +753,8 @@
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
 {
 	const struct commit *a = a_, *b = b_;
-	const uint32_t generation_a = commit_graph_generation(a),
-		       generation_b = commit_graph_generation(b);
+	const timestamp_t generation_a = commit_graph_generation(a),
+			  generation_b = commit_graph_generation(b);
 
 	/* newer commits first */
 	if (generation_a < generation_b)
@@ -973,7 +995,7 @@
 	"gpgsig-sha256",
 };
 
-static int do_sign_commit(struct strbuf *buf, const char *keyid)
+int sign_with_header(struct strbuf *buf, const char *keyid)
 {
 	struct strbuf sig = STRBUF_INIT;
 	int inspos, copypos;
@@ -1013,21 +1035,32 @@
 	return 0;
 }
 
-int parse_signed_commit(const struct commit *commit,
-			struct strbuf *payload, struct strbuf *signature)
-{
 
+
+int parse_signed_commit(const struct commit *commit,
+			struct strbuf *payload, struct strbuf *signature,
+			const struct git_hash_algo *algop)
+{
 	unsigned long size;
 	const char *buffer = get_commit_buffer(commit, &size);
-	int in_signature, saw_signature = -1;
-	const char *line, *tail;
-	const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
-	int gpg_sig_header_len = strlen(gpg_sig_header);
+	int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
+
+	unuse_commit_buffer(commit, buffer);
+	return ret;
+}
+
+int parse_buffer_signed_by_header(const char *buffer,
+				  unsigned long size,
+				  struct strbuf *payload,
+				  struct strbuf *signature,
+				  const struct git_hash_algo *algop)
+{
+	int in_signature = 0, saw_signature = 0, other_signature = 0;
+	const char *line, *tail, *p;
+	const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algop)];
 
 	line = buffer;
 	tail = buffer + size;
-	in_signature = 0;
-	saw_signature = 0;
 	while (line < tail) {
 		const char *sig = NULL;
 		const char *next = memchr(line, '\n', tail - line);
@@ -1035,9 +1068,15 @@
 		next = next ? next + 1 : tail;
 		if (in_signature && line[0] == ' ')
 			sig = line + 1;
-		else if (starts_with(line, gpg_sig_header) &&
-			 line[gpg_sig_header_len] == ' ')
-			sig = line + gpg_sig_header_len + 1;
+		else if (skip_prefix(line, gpg_sig_header, &p) &&
+			 *p == ' ') {
+			sig = line + strlen(gpg_sig_header) + 1;
+			other_signature = 0;
+		}
+		else if (starts_with(line, "gpgsig"))
+			other_signature = 1;
+		else if (other_signature && line[0] != ' ')
+			other_signature = 0;
 		if (sig) {
 			strbuf_add(signature, sig, next - sig);
 			saw_signature = 1;
@@ -1046,12 +1085,12 @@
 			if (*line == '\n')
 				/* dump the whole remainder of the buffer */
 				next = tail;
-			strbuf_add(payload, line, next - line);
+			if (!other_signature)
+				strbuf_add(payload, line, next - line);
 			in_signature = 0;
 		}
 		line = next;
 	}
-	unuse_commit_buffer(commit, buffer);
 	return saw_signature;
 }
 
@@ -1060,23 +1099,29 @@
 	const char *line = buf->buf;
 	const char *tail = buf->buf + buf->len;
 	int in_signature = 0;
-	const char *sig_start = NULL;
-	const char *sig_end = NULL;
+	struct sigbuf {
+		const char *start;
+		const char *end;
+	} sigs[2], *sigp = &sigs[0];
+	int i;
+	const char *orig_buf = buf->buf;
+
+	memset(sigs, 0, sizeof(sigs));
 
 	while (line < tail) {
 		const char *next = memchr(line, '\n', tail - line);
 		next = next ? next + 1 : tail;
 
 		if (in_signature && line[0] == ' ')
-			sig_end = next;
+			sigp->end = next;
 		else if (starts_with(line, "gpgsig")) {
 			int i;
 			for (i = 1; i < GIT_HASH_NALGOS; i++) {
 				const char *p;
 				if (skip_prefix(line, gpg_sig_headers[i], &p) &&
 				    *p == ' ') {
-					sig_start = line;
-					sig_end = next;
+					sigp->start = line;
+					sigp->end = next;
 					in_signature = 1;
 				}
 			}
@@ -1084,15 +1129,18 @@
 			if (*line == '\n')
 				/* dump the whole remainder of the buffer */
 				next = tail;
+			if (in_signature && sigp - sigs != ARRAY_SIZE(sigs))
+				sigp++;
 			in_signature = 0;
 		}
 		line = next;
 	}
 
-	if (sig_start)
-		strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
+	for (i = ARRAY_SIZE(sigs) - 1; i >= 0; i--)
+		if (sigs[i].start)
+			strbuf_remove(buf, sigs[i].start - orig_buf, sigs[i].end - sigs[i].start);
 
-	return sig_start != NULL;
+	return sigs[0].start != NULL;
 }
 
 static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
@@ -1100,8 +1148,10 @@
 	struct merge_remote_desc *desc;
 	struct commit_extra_header *mergetag;
 	char *buf;
-	unsigned long size, len;
+	unsigned long size;
 	enum object_type type;
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
 
 	desc = merge_remote_util(parent);
 	if (!desc || !desc->obj)
@@ -1109,8 +1159,7 @@
 	buf = read_object_file(&desc->obj->oid, &type, &size);
 	if (!buf || type != OBJ_TAG)
 		goto free_return;
-	len = parse_signature(buf, size);
-	if (size == len)
+	if (!parse_signature(buf, size, &payload, &signature))
 		goto free_return;
 	/*
 	 * We could verify this signature and either omit the tag when
@@ -1129,6 +1178,8 @@
 
 	**tail = mergetag;
 	*tail = &mergetag->next;
+	strbuf_release(&payload);
+	strbuf_release(&signature);
 	return;
 
 free_return:
@@ -1143,7 +1194,7 @@
 
 	sigc->result = 'N';
 
-	if (parse_signed_commit(commit, &payload, &signature) <= 0)
+	if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
 		goto out;
 	ret = check_signature(payload.buf, payload.len, signature.buf,
 		signature.len, sigc);
@@ -1493,7 +1544,7 @@
 	if (encoding_is_utf8 && !verify_utf8(&buffer))
 		fprintf(stderr, _(commit_utf8_warn));
 
-	if (sign_commit && do_sign_commit(&buffer, sign_commit)) {
+	if (sign_commit && sign_with_header(&buffer, sign_commit)) {
 		result = -1;
 		goto out;
 	}
diff --git a/commit.h b/commit.h
index 5467786..49c0f50 100644
--- a/commit.h
+++ b/commit.h
@@ -11,9 +11,10 @@
 #include "commit-slab.h"
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
-#define GENERATION_NUMBER_INFINITY 0xFFFFFFFF
-#define GENERATION_NUMBER_MAX 0x3FFFFFFF
+#define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
+#define GENERATION_NUMBER_V1_MAX 0x3FFFFFFF
 #define GENERATION_NUMBER_ZERO 0
+#define GENERATION_NUMBER_V2_OFFSET_MAX ((1ULL << 31) - 1)
 
 struct commit_list {
 	struct commit *item;
@@ -88,9 +89,10 @@
 	return repo_parse_commit_gently(r, item, 0);
 }
 
-static inline int parse_commit_no_graph(struct commit *commit)
+static inline int repo_parse_commit_no_graph(struct repository *r,
+					     struct commit *commit)
 {
-	return repo_parse_commit_internal(the_repository, commit, 0, 0);
+	return repo_parse_commit_internal(r, commit, 0, 0);
 }
 
 #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
@@ -167,6 +169,8 @@
 
 struct commit_list *commit_list_insert(struct commit *item,
 					struct commit_list **list);
+int commit_list_contains(struct commit *item,
+			 struct commit_list *list);
 struct commit_list **commit_list_append(struct commit *commit,
 					struct commit_list **next);
 unsigned commit_list_count(const struct commit_list *l);
@@ -177,6 +181,9 @@
 /* Shallow copy of the input list */
 struct commit_list *copy_commit_list(struct commit_list *list);
 
+/* Modify list in-place to reverse it, returning new head; list will be tail */
+struct commit_list *reverse_commit_list(struct commit_list *list);
+
 void free_commit_list(struct commit_list *list);
 
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
@@ -234,7 +241,7 @@
 
 struct commit_graft *read_graft_line(struct strbuf *line);
 /* commit_graft_pos returns an index into r->parsed_objects->grafts. */
-int commit_graft_pos(struct repository *r, const unsigned char *sha1);
+int commit_graft_pos(struct repository *r, const struct object_id *oid);
 int register_commit_graft(struct repository *r, struct commit_graft *, int);
 void prepare_commit_graft(struct repository *r);
 struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
@@ -312,7 +319,8 @@
 struct commit *get_merge_parent(const char *name);
 
 int parse_signed_commit(const struct commit *commit,
-			struct strbuf *message, struct strbuf *signature);
+			struct strbuf *message, struct strbuf *signature,
+			const struct git_hash_algo *algop);
 int remove_signature(struct strbuf *buf);
 
 /*
@@ -354,4 +362,13 @@
 LAST_ARG_MUST_BE_NULL
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
 
+/* Sign a commit or tag buffer, storing the result in a header. */
+int sign_with_header(struct strbuf *buf, const char *keyid);
+/* Parse the signature out of a header. */
+int parse_buffer_signed_by_header(const char *buffer,
+				  unsigned long size,
+				  struct strbuf *payload,
+				  struct strbuf *signature,
+				  const struct git_hash_algo *algop);
+
 #endif /* COMMIT_H */
diff --git a/compat/mingw.h b/compat/mingw.h
index af8eddd..c9a52ad 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -227,6 +227,7 @@
 
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
+#undef OPEN_RETURNS_EINTR
 
 int mingw_fgetc(FILE *stream);
 #define fgetc mingw_fgetc
diff --git a/compat/open.c b/compat/open.c
new file mode 100644
index 0000000..eb3754a
--- /dev/null
+++ b/compat/open.c
@@ -0,0 +1,25 @@
+#include "git-compat-util.h"
+
+#undef open
+int git_open_with_retry(const char *path, int flags, ...)
+{
+	mode_t mode = 0;
+	int ret;
+
+	/*
+	 * Also O_TMPFILE would take a mode, but it isn't defined everywhere.
+	 * And anyway, we don't use it in our code base.
+	 */
+	if (flags & O_CREAT) {
+		va_list ap;
+		va_start(ap, flags);
+		mode = va_arg(ap, int);
+		va_end(ap);
+	}
+
+	do {
+		ret = open(path, flags, mode);
+	} while (ret < 0 && errno == EINTR);
+
+	return ret;
+}
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 136250f..ec56056 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -60,32 +60,46 @@
 	strbuf_release(&path);
 }
 
+static inline const char *precompose_string_if_needed(const char *in)
+{
+	size_t inlen;
+	size_t outlen;
+	if (has_non_ascii(in, (size_t)-1, &inlen)) {
+		iconv_t ic_prec;
+		char *out;
+		if (precomposed_unicode < 0)
+			git_config_get_bool("core.precomposeunicode", &precomposed_unicode);
+		if (precomposed_unicode != 1)
+			return in;
+		ic_prec = iconv_open(repo_encoding, path_encoding);
+		if (ic_prec == (iconv_t) -1)
+			return in;
 
-void precompose_argv(int argc, const char **argv)
+		out = reencode_string_iconv(in, inlen, ic_prec, 0, &outlen);
+		if (out) {
+			if (outlen == inlen && !memcmp(in, out, outlen))
+				free(out); /* no need to return indentical */
+			else
+				in = out;
+		}
+		iconv_close(ic_prec);
+
+	}
+	return in;
+}
+
+const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix)
 {
 	int i = 0;
-	const char *oldarg;
-	char *newarg;
-	iconv_t ic_precompose;
-
-	if (precomposed_unicode != 1)
-		return;
-
-	ic_precompose = iconv_open(repo_encoding, path_encoding);
-	if (ic_precompose == (iconv_t) -1)
-		return;
 
 	while (i < argc) {
-		size_t namelen;
-		oldarg = argv[i];
-		if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
-			newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, 0, NULL);
-			if (newarg)
-				argv[i] = newarg;
-		}
+		argv[i] = precompose_string_if_needed(argv[i]);
 		i++;
 	}
-	iconv_close(ic_precompose);
+	if (prefix) {
+		prefix = precompose_string_if_needed(prefix);
+	}
+	return prefix;
 }
 
 
diff --git a/compat/precompose_utf8.h b/compat/precompose_utf8.h
index 6f843d3..d70b846 100644
--- a/compat/precompose_utf8.h
+++ b/compat/precompose_utf8.h
@@ -28,7 +28,7 @@
 	struct dirent_prec_psx *dirent_nfc;
 } PREC_DIR;
 
-void precompose_argv(int argc, const char **argv);
+const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix);
 void probe_utf8_pathname_composition(void);
 
 PREC_DIR *precompose_utf8_opendir(const char *dirname);
diff --git a/config.c b/config.c
index 1137bd7..f90b633 100644
--- a/config.c
+++ b/config.c
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "branch.h"
 #include "config.h"
+#include "environment.h"
 #include "repository.h"
 #include "lockfile.h"
 #include "exec-cmd.h"
@@ -332,7 +333,7 @@
 	return ret;
 }
 
-void git_config_push_parameter(const char *text)
+static void git_config_push_split_parameter(const char *key, const char *value)
 {
 	struct strbuf env = STRBUF_INIT;
 	const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
@@ -340,11 +341,74 @@
 		strbuf_addstr(&env, old);
 		strbuf_addch(&env, ' ');
 	}
-	sq_quote_buf(&env, text);
+	sq_quote_buf(&env, key);
+	strbuf_addch(&env, '=');
+	if (value)
+		sq_quote_buf(&env, value);
 	setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
 	strbuf_release(&env);
 }
 
+void git_config_push_parameter(const char *text)
+{
+	const char *value;
+
+	/*
+	 * When we see:
+	 *
+	 *   section.subsection=with=equals.key=value
+	 *
+	 * we cannot tell if it means:
+	 *
+	 *   [section "subsection=with=equals"]
+	 *   key = value
+	 *
+	 * or:
+	 *
+	 *   [section]
+	 *   subsection = with=equals.key=value
+	 *
+	 * We parse left-to-right for the first "=", meaning we'll prefer to
+	 * keep the value intact over the subsection. This is historical, but
+	 * also sensible since values are more likely to contain odd or
+	 * untrusted input than a section name.
+	 *
+	 * A missing equals is explicitly allowed (as a bool-only entry).
+	 */
+	value = strchr(text, '=');
+	if (value) {
+		char *key = xmemdupz(text, value - text);
+		git_config_push_split_parameter(key, value + 1);
+		free(key);
+	} else {
+		git_config_push_split_parameter(text, NULL);
+	}
+}
+
+void git_config_push_env(const char *spec)
+{
+	char *key;
+	const char *env_name;
+	const char *env_value;
+
+	env_name = strrchr(spec, '=');
+	if (!env_name)
+		die(_("invalid config format: %s"), spec);
+	key = xmemdupz(spec, env_name - spec);
+	env_name++;
+	if (!*env_name)
+		die(_("missing environment variable name for configuration '%.*s'"),
+		    (int)(env_name - spec - 1), spec);
+
+	env_value = getenv(env_name);
+	if (!env_value)
+		die(_("missing environment variable '%s' for configuration '%.*s'"),
+		    env_name, (int)(env_name - spec - 1), spec);
+
+	git_config_push_split_parameter(key, env_value);
+	free(key);
+}
+
 static inline int iskeychar(int c)
 {
 	return isalnum(c) || c == '-';
@@ -437,11 +501,26 @@
 	return !git_config_parse_key_1(key, NULL, NULL, 1);
 }
 
+static int config_parse_pair(const char *key, const char *value,
+			  config_fn_t fn, void *data)
+{
+	char *canonical_name;
+	int ret;
+
+	if (!strlen(key))
+		return error(_("empty config key"));
+	if (git_config_parse_key(key, &canonical_name, NULL))
+		return -1;
+
+	ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
+	free(canonical_name);
+	return ret;
+}
+
 int git_config_parse_parameter(const char *text,
 			       config_fn_t fn, void *data)
 {
 	const char *value;
-	char *canonical_name;
 	struct strbuf **pair;
 	int ret;
 
@@ -462,51 +541,131 @@
 		return error(_("bogus config parameter: %s"), text);
 	}
 
-	if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) {
-		ret = -1;
-	} else {
-		ret = (fn(canonical_name, value, data) < 0) ? -1 : 0;
-		free(canonical_name);
-	}
+	ret = config_parse_pair(pair[0]->buf, value, fn, data);
 	strbuf_list_free(pair);
 	return ret;
 }
 
+static int parse_config_env_list(char *env, config_fn_t fn, void *data)
+{
+	char *cur = env;
+	while (cur && *cur) {
+		const char *key = sq_dequote_step(cur, &cur);
+		if (!key)
+			return error(_("bogus format in %s"),
+				     CONFIG_DATA_ENVIRONMENT);
+
+		if (!cur || isspace(*cur)) {
+			/* old-style 'key=value' */
+			if (git_config_parse_parameter(key, fn, data) < 0)
+				return -1;
+		}
+		else if (*cur == '=') {
+			/* new-style 'key'='value' */
+			const char *value;
+
+			cur++;
+			if (*cur == '\'') {
+				/* quoted value */
+				value = sq_dequote_step(cur, &cur);
+				if (!value || (cur && !isspace(*cur))) {
+					return error(_("bogus format in %s"),
+						     CONFIG_DATA_ENVIRONMENT);
+				}
+			} else if (!*cur || isspace(*cur)) {
+				/* implicit bool: 'key'= */
+				value = NULL;
+			} else {
+				return error(_("bogus format in %s"),
+					     CONFIG_DATA_ENVIRONMENT);
+			}
+
+			if (config_parse_pair(key, value, fn, data) < 0)
+				return -1;
+		}
+		else {
+			/* unknown format */
+			return error(_("bogus format in %s"),
+				     CONFIG_DATA_ENVIRONMENT);
+		}
+
+		if (cur) {
+			while (isspace(*cur))
+				cur++;
+		}
+	}
+	return 0;
+}
+
 int git_config_from_parameters(config_fn_t fn, void *data)
 {
-	const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
+	const char *env;
+	struct strbuf envvar = STRBUF_INIT;
+	struct strvec to_free = STRVEC_INIT;
 	int ret = 0;
-	char *envw;
-	const char **argv = NULL;
-	int nr = 0, alloc = 0;
-	int i;
+	char *envw = NULL;
 	struct config_source source;
 
-	if (!env)
-		return 0;
-
 	memset(&source, 0, sizeof(source));
 	source.prev = cf;
 	source.origin_type = CONFIG_ORIGIN_CMDLINE;
 	cf = &source;
 
-	/* sq_dequote will write over it */
-	envw = xstrdup(env);
+	env = getenv(CONFIG_COUNT_ENVIRONMENT);
+	if (env) {
+		unsigned long count;
+		char *endp;
+		int i;
 
-	if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
-		ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT);
-		goto out;
+		count = strtoul(env, &endp, 10);
+		if (*endp) {
+			ret = error(_("bogus count in %s"), CONFIG_COUNT_ENVIRONMENT);
+			goto out;
+		}
+		if (count > INT_MAX) {
+			ret = error(_("too many entries in %s"), CONFIG_COUNT_ENVIRONMENT);
+			goto out;
+		}
+
+		for (i = 0; i < count; i++) {
+			const char *key, *value;
+
+			strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i);
+			key = getenv_safe(&to_free, envvar.buf);
+			if (!key) {
+				ret = error(_("missing config key %s"), envvar.buf);
+				goto out;
+			}
+			strbuf_reset(&envvar);
+
+			strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i);
+			value = getenv_safe(&to_free, envvar.buf);
+			if (!value) {
+				ret = error(_("missing config value %s"), envvar.buf);
+				goto out;
+			}
+			strbuf_reset(&envvar);
+
+			if (config_parse_pair(key, value, fn, data) < 0) {
+				ret = -1;
+				goto out;
+			}
+		}
 	}
 
-	for (i = 0; i < nr; i++) {
-		if (git_config_parse_parameter(argv[i], fn, data) < 0) {
+	env = getenv(CONFIG_DATA_ENVIRONMENT);
+	if (env) {
+		/* sq_dequote will write over it */
+		envw = xstrdup(env);
+		if (parse_config_env_list(envw, fn, data) < 0) {
 			ret = -1;
 			goto out;
 		}
 	}
 
 out:
-	free(argv);
+	strbuf_release(&envvar);
+	strvec_clear(&to_free);
 	free(envw);
 	cf = source.prev;
 	return ret;
@@ -996,15 +1155,6 @@
 	if (!value)
 		value = "";
 
-	if (!strcmp(name, "GIT_TEST_GETTEXT_POISON"))
-		/*
-		 * We explicitly *don't* use _() here since it would
-		 * cause an infinite loop with _() needing to call
-		 * use_gettext_poison(). This is why marked up
-		 * translations with N_() above.
-		 */
-		die(bad_numeric, value, name, error_type);
-
 	if (!(cf && cf->name))
 		die(_(bad_numeric), value, name, _(error_type));
 
@@ -1030,6 +1180,20 @@
 	}
 }
 
+NORETURN
+static void die_bad_bool(const char *name, const char *value)
+{
+	if (!strcmp(name, "GIT_TEST_GETTEXT_POISON"))
+		/*
+		 * We explicitly *don't* use _() here since it would
+		 * cause an infinite loop with _() needing to call
+		 * use_gettext_poison().
+		 */
+		die("bad boolean config value '%s' for '%s'", value, name);
+	else
+		die(_("bad boolean config value '%s' for '%s'"), value, name);
+}
+
 int git_config_int(const char *name, const char *value)
 {
 	int ret;
@@ -1102,8 +1266,10 @@
 
 int git_config_bool(const char *name, const char *value)
 {
-	int discard;
-	return !!git_config_bool_or_int(name, value, &discard);
+	int v = git_parse_maybe_bool(value);
+	if (v < 0)
+		die_bad_bool(name, value);
+	return v;
 }
 
 int git_config_string(const char **dest, const char *var, const char *value)
@@ -1217,6 +1383,8 @@
 			return config_error_nonbool(var);
 		if (!strcasecmp(value, "auto"))
 			default_abbrev = -1;
+		else if (!git_parse_maybe_bool_text(value))
+			default_abbrev = the_hash_algo->hexsz;
 		else {
 			int abbrev = git_config_int(var, value);
 			if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
diff --git a/config.h b/config.h
index c1449bb..19a9adb 100644
--- a/config.h
+++ b/config.h
@@ -138,6 +138,7 @@
 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);
+void git_config_push_env(const char *spec);
 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);
diff --git a/config.mak.uname b/config.mak.uname
index 198ab1e..d204c20 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -124,6 +124,9 @@
 	ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 11 && echo 1),1)
 		HAVE_GETDELIM = YesPlease
 	endif
+	ifeq ($(shell test "`expr "$(uname_R)" : '\([0-9][0-9]*\)\.'`" -ge 20 && echo 1),1)
+		OPEN_RETURNS_EINTR = UnfortunatelyYes
+	endif
 	NO_MEMMEM = YesPlease
 	USE_ST_TIMESPEC = YesPlease
 	HAVE_DEV_TTY = YesPlease
@@ -664,7 +667,6 @@
 		NO_GETTEXT =
 		USE_GETTEXT_SCHEME = fallthrough
 		USE_LIBPCRE= YesPlease
-		NO_LIBPCRE1_JIT = UnfortunatelyYes
 		NO_CURL =
 		USE_NED_ALLOCATOR = YesPlease
 	else
diff --git a/configure.ac b/configure.ac
index 66aedb9..031e8d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -252,16 +252,17 @@
 # Define USE_LIBPCRE if you have and want to use libpcre. Various
 # commands such as log and grep offer runtime options to use
 # Perl-compatible regular expressions instead of standard or extended
-# POSIX regular expressions.
-#
-# USE_LIBPCRE is a synonym for USE_LIBPCRE2, define USE_LIBPCRE1
-# instead if you'd like to use the legacy version 1 of the PCRE
-# library. Support for version 1 will likely be removed in some future
-# release of Git, as upstream has all but abandoned it.
+# POSIX regular expressions. Only libpcre version 2 is supported.
 #
 # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are in
 # /foo/bar/include and /foo/bar/lib directories.
 #
+
+AC_ARG_WITH(libpcre1,
+AS_HELP_STRING([--with-libpcre1],[DEPRECATED]),
+    AC_MSG_ERROR([support for --with-libpcre1 has been removed. Use --with-libpcre2!])
+    )
+
 AC_ARG_WITH(libpcre,
 AS_HELP_STRING([--with-libpcre],[synonym for --with-libpcre2]),
     if test "$withval" = "no"; then
@@ -277,22 +278,6 @@
 	GIT_CONF_SUBST([LIBPCREDIR])
     fi)
 
-AC_ARG_WITH(libpcre1,
-AS_HELP_STRING([--with-libpcre1],[support Perl-compatible regexes via libpcre1 (default is NO)])
-AS_HELP_STRING([],           [ARG can be also prefix for libpcre library and headers]),
-    if test "$withval" = "no"; then
-	USE_LIBPCRE1=
-    elif test "$withval" = "yes"; then
-	USE_LIBPCRE1=YesPlease
-    else
-	USE_LIBPCRE1=YesPlease
-	LIBPCREDIR=$withval
-	AC_MSG_NOTICE([Setting LIBPCREDIR to $LIBPCREDIR])
-        dnl USE_LIBPCRE1 can still be modified below, so don't substitute
-        dnl it yet.
-	GIT_CONF_SUBST([LIBPCREDIR])
-    fi)
-
 AC_ARG_WITH(libpcre2,
 AS_HELP_STRING([--with-libpcre2],[support Perl-compatible regexes via libpcre2 (default is NO)])
 AS_HELP_STRING([],           [ARG can be also prefix for libpcre library and headers]),
@@ -300,10 +285,6 @@
         AC_MSG_ERROR([Only supply one of --with-libpcre or its synonym --with-libpcre2!])
     fi
 
-    if test -n "$USE_LIBPCRE1"; then
-        AC_MSG_ERROR([Only supply one of --with-libpcre1 or --with-libpcre2!])
-    fi
-
     if test "$withval" = "no"; then
 	USE_LIBPCRE2=
     elif test "$withval" = "yes"; then
@@ -554,25 +535,9 @@
 GIT_CONF_SUBST([NO_OPENSSL])
 
 #
-# Handle the USE_LIBPCRE1 and USE_LIBPCRE2 options potentially set
-# above.
+# Handle the USE_LIBPCRE options potentially set above.
 #
 
-if test -n "$USE_LIBPCRE1"; then
-
-GIT_STASH_FLAGS($LIBPCREDIR)
-
-AC_CHECK_LIB([pcre], [pcre_version],
-[USE_LIBPCRE1=YesPlease],
-[USE_LIBPCRE1=])
-
-GIT_UNSTASH_FLAGS($LIBPCREDIR)
-
-GIT_CONF_SUBST([USE_LIBPCRE1])
-
-fi
-
-
 if test -n "$USE_LIBPCRE2"; then
 
 GIT_STASH_FLAGS($LIBPCREDIR)
diff --git a/connect.c b/connect.c
index 9c97fee..40b5c15 100644
--- a/connect.c
+++ b/connect.c
@@ -376,7 +376,8 @@
 }
 
 /* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
-static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
+static int process_ref_v2(struct packet_reader *reader, struct ref ***list,
+			  char **unborn_head_target)
 {
 	int ret = 1;
 	int i = 0;
@@ -397,6 +398,25 @@
 		goto out;
 	}
 
+	if (!strcmp("unborn", line_sections.items[i].string)) {
+		i++;
+		if (unborn_head_target &&
+		    !strcmp("HEAD", line_sections.items[i++].string)) {
+			/*
+			 * Look for the symref target (if any). If found,
+			 * return it to the caller.
+			 */
+			for (; i < line_sections.nr; i++) {
+				const char *arg = line_sections.items[i].string;
+
+				if (skip_prefix(arg, "symref-target:", &arg)) {
+					*unborn_head_target = xstrdup(arg);
+					break;
+				}
+			}
+		}
+		goto out;
+	}
 	if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) ||
 	    *end) {
 		ret = 0;
@@ -453,12 +473,16 @@
 
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
 			     struct ref **list, int for_push,
-			     const struct strvec *ref_prefixes,
+			     struct transport_ls_refs_options *transport_options,
 			     const struct string_list *server_options,
 			     int stateless_rpc)
 {
 	int i;
 	const char *hash_name;
+	struct strvec *ref_prefixes = transport_options ?
+		&transport_options->ref_prefixes : NULL;
+	char **unborn_head_target = transport_options ?
+		&transport_options->unborn_head_target : NULL;
 	*list = NULL;
 
 	if (server_supports_v2("ls-refs", 1))
@@ -488,6 +512,8 @@
 	if (!for_push)
 		packet_write_fmt(fd_out, "peel\n");
 	packet_write_fmt(fd_out, "symrefs\n");
+	if (server_supports_feature("ls-refs", "unborn", 0))
+		packet_write_fmt(fd_out, "unborn\n");
 	for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) {
 		packet_write_fmt(fd_out, "ref-prefix %s\n",
 				 ref_prefixes->v[i]);
@@ -496,7 +522,7 @@
 
 	/* Process response from server */
 	while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
-		if (!process_ref_v2(reader, &list))
+		if (!process_ref_v2(reader, &list, unborn_head_target))
 			die(_("invalid ls-refs response: %s"), reader->line);
 	}
 
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index c151dd7..ac3dbc0 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -910,9 +910,7 @@
 set(TAR tar)
 set(NO_CURL )
 set(NO_EXPAT )
-set(USE_LIBPCRE1 )
 set(USE_LIBPCRE2 )
-set(NO_LIBPCRE1_JIT )
 set(NO_PERL )
 set(NO_PTHREADS )
 set(NO_PYTHON )
@@ -949,8 +947,6 @@
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "TAR='${TAR}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_CURL='${NO_CURL}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_EXPAT='${NO_EXPAT}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "USE_LIBPCRE1='${USE_LIBPCRE1}'\n")
-file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_LIBPCRE1_JIT='${NO_LIBPCRE1_JIT}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PERL='${NO_PERL}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PTHREADS='${NO_PTHREADS}'\n")
 file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_UNIX_SOCKETS='${NO_UNIX_SOCKETS}'\n")
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 463a312..7dc6cd8 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -29,6 +29,15 @@
 # tell the completion to use commit completion.  This also works with aliases
 # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
 #
+# If you have a command that is not part of git, but you would still
+# like completion, you can use __git_complete:
+#
+#   __git_complete gl git_log
+#
+# Or if it's a main command (i.e. git or gitk):
+#
+#   __git_complete gk gitk
+#
 # Compatible with bash 3.2.57.
 #
 # You can set the following environment variables to influence the behavior of
@@ -1438,8 +1447,10 @@
 	while [ $c -lt $cword ]; do
 		i="${words[c]}"
 		case "$i" in
-		-d|--delete|-m|--move)	only_local_ref="y" ;;
-		-r|--remotes)		has_r="y" ;;
+		-d|-D|--delete|-m|-M|--move|-c|-C|--copy)
+			only_local_ref="y" ;;
+		-r|--remotes)
+			has_r="y" ;;
 		esac
 		((c++))
 	done
@@ -3358,15 +3369,19 @@
 	esac
 }
 
+__git_have_func () {
+	declare -f -- "$1" >/dev/null 2>&1
+}
+
 __git_complete_command () {
 	local command="$1"
 	local completion_func="_git_${command//-/_}"
-	if ! declare -f $completion_func >/dev/null 2>/dev/null &&
-		declare -f _completion_loader >/dev/null 2>/dev/null
+	if ! __git_have_func $completion_func &&
+		__git_have_func _completion_loader
 	then
 		_completion_loader "git-$command"
 	fi
-	if declare -f $completion_func >/dev/null 2>/dev/null
+	if __git_have_func $completion_func
 	then
 		$completion_func
 		return 0
@@ -3493,10 +3508,7 @@
 	$1
 }
 
-# Setup completion for certain functions defined above by setting common
-# variables and workarounds.
-# This is NOT a public function; use at your own risk.
-__git_complete ()
+___git_complete ()
 {
 	local wrapper="__git_wrap${2}"
 	eval "$wrapper () { __git_func_wrap $2 ; }"
@@ -3504,13 +3516,33 @@
 		|| complete -o default -o nospace -F $wrapper $1
 }
 
-__git_complete git __git_main
-__git_complete gitk __gitk_main
+# Setup the completion for git commands
+# 1: command or alias
+# 2: function to call (e.g. `git`, `gitk`, `git_fetch`)
+__git_complete ()
+{
+	local func
+
+	if __git_have_func $2; then
+		func=$2
+	elif __git_have_func __$2_main; then
+		func=__$2_main
+	elif __git_have_func _$2; then
+		func=_$2
+	else
+		echo "ERROR: could not find function '$2'" 1>&2
+		return 1
+	fi
+	___git_complete $1 $func
+}
+
+___git_complete git __git_main
+___git_complete gitk __gitk_main
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
 if [ "$OSTYPE" = cygwin ]; then
-	__git_complete git.exe __git_main
+	___git_complete git.exe __git_main
 fi
diff --git a/diff-merges.c b/diff-merges.c
new file mode 100644
index 0000000..146bb50
--- /dev/null
+++ b/diff-merges.c
@@ -0,0 +1,146 @@
+#include "diff-merges.h"
+
+#include "revision.h"
+
+static void suppress(struct rev_info *revs)
+{
+	revs->separate_merges = 0;
+	revs->first_parent_merges = 0;
+	revs->combine_merges = 0;
+	revs->dense_combined_merges = 0;
+	revs->combined_all_paths = 0;
+	revs->combined_imply_patch = 0;
+	revs->merges_need_diff = 0;
+}
+
+static void set_separate(struct rev_info *revs)
+{
+	suppress(revs);
+	revs->separate_merges = 1;
+}
+
+static void set_first_parent(struct rev_info *revs)
+{
+	set_separate(revs);
+	revs->first_parent_merges = 1;
+}
+
+static void set_m(struct rev_info *revs)
+{
+	/*
+	 * To "diff-index", "-m" means "match missing", and to the "log"
+	 * family of commands, it means "show full diff for merges". Set
+	 * both fields appropriately.
+	 */
+	set_separate(revs);
+	revs->match_missing = 1;
+}
+
+static void set_combined(struct rev_info *revs)
+{
+	suppress(revs);
+	revs->combine_merges = 1;
+	revs->dense_combined_merges = 0;
+}
+
+static void set_dense_combined(struct rev_info *revs)
+{
+	suppress(revs);
+	revs->combine_merges = 1;
+	revs->dense_combined_merges = 1;
+}
+
+static void set_diff_merges(struct rev_info *revs, const char *optarg)
+{
+	if (!strcmp(optarg, "off") || !strcmp(optarg, "none")) {
+		suppress(revs);
+		/* Return early to leave revs->merges_need_diff unset */
+		return;
+	}
+
+	if (!strcmp(optarg, "1") || !strcmp(optarg, "first-parent"))
+		set_first_parent(revs);
+	else if (!strcmp(optarg, "m") || !strcmp(optarg, "separate"))
+		set_separate(revs);
+	else if (!strcmp(optarg, "c") || !strcmp(optarg, "combined"))
+		set_combined(revs);
+	else if (!strcmp(optarg, "cc") || !strcmp(optarg, "dense-combined"))
+		set_dense_combined(revs);
+	else
+		die(_("unknown value for --diff-merges: %s"), optarg);
+
+	/* The flag is cleared by set_xxx() functions, so don't move this up */
+	revs->merges_need_diff = 1;
+}
+
+/*
+ * Public functions. They are in the order they are called.
+ */
+
+int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
+{
+	int argcount = 1;
+	const char *optarg;
+	const char *arg = argv[0];
+
+	if (!strcmp(arg, "-m")) {
+		set_m(revs);
+	} else if (!strcmp(arg, "-c")) {
+		set_combined(revs);
+		revs->combined_imply_patch = 1;
+	} else if (!strcmp(arg, "--cc")) {
+		set_dense_combined(revs);
+		revs->combined_imply_patch = 1;
+	} else if (!strcmp(arg, "--no-diff-merges")) {
+		suppress(revs);
+	} else if (!strcmp(arg, "--combined-all-paths")) {
+		revs->combined_all_paths = 1;
+	} else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
+		set_diff_merges(revs, optarg);
+	} else
+		return 0;
+
+	revs->explicit_diff_merges = 1;
+	return argcount;
+}
+
+void diff_merges_suppress(struct rev_info *revs)
+{
+	suppress(revs);
+}
+
+void diff_merges_default_to_first_parent(struct rev_info *revs)
+{
+	if (!revs->explicit_diff_merges)
+		revs->separate_merges = 1;
+	if (revs->separate_merges)
+		revs->first_parent_merges = 1;
+}
+
+void diff_merges_default_to_dense_combined(struct rev_info *revs)
+{
+	if (!revs->explicit_diff_merges)
+		set_dense_combined(revs);
+}
+
+void diff_merges_set_dense_combined_if_unset(struct rev_info *revs)
+{
+	if (!revs->combine_merges)
+		set_dense_combined(revs);
+}
+
+void diff_merges_setup_revs(struct rev_info *revs)
+{
+	if (revs->combine_merges == 0)
+		revs->dense_combined_merges = 0;
+	if (revs->separate_merges == 0)
+		revs->first_parent_merges = 0;
+	if (revs->combined_all_paths && !revs->combine_merges)
+		die("--combined-all-paths makes no sense without -c or --cc");
+	if (revs->combined_imply_patch)
+		revs->diff = 1;
+	if (revs->combined_imply_patch || revs->merges_need_diff) {
+		if (!revs->diffopt.output_format)
+			revs->diffopt.output_format = DIFF_FORMAT_PATCH;
+	}
+}
diff --git a/diff-merges.h b/diff-merges.h
new file mode 100644
index 0000000..659467c
--- /dev/null
+++ b/diff-merges.h
@@ -0,0 +1,24 @@
+#ifndef DIFF_MERGES_H
+#define DIFF_MERGES_H
+
+/*
+ * diff-merges - utility module to handle command-line options for
+ * selection of particular diff format of merge commits
+ * representation.
+ */
+
+struct rev_info;
+
+int diff_merges_parse_opts(struct rev_info *revs, const char **argv);
+
+void diff_merges_suppress(struct rev_info *revs);
+
+void diff_merges_default_to_first_parent(struct rev_info *revs);
+
+void diff_merges_default_to_dense_combined(struct rev_info *revs);
+
+void diff_merges_set_dense_combined_if_unset(struct rev_info *revs);
+
+void diff_merges_setup_revs(struct rev_info *revs);
+
+#endif
diff --git a/diff.c b/diff.c
index 2253ec8..6956f5e 100644
--- a/diff.c
+++ b/diff.c
@@ -4593,6 +4593,9 @@
 
 	options->orderfile = diff_order_file_cfg;
 
+	if (!options->flags.ignore_submodule_set)
+		options->flags.ignore_untracked_in_submodules = 1;
+
 	if (diff_no_prefix) {
 		options->a_prefix = options->b_prefix = "";
 	} else if (!diff_mnemonic_prefix) {
@@ -5345,6 +5348,19 @@
 	return 0;
 }
 
+static int diff_opt_rotate_to(const struct option *opt, const char *arg, int unset)
+{
+	struct diff_options *options = opt->value;
+
+	BUG_ON_OPT_NEG(unset);
+	if (!strcmp(opt->long_name, "skip-to"))
+		options->skip_instead_of_rotate = 1;
+	else
+		options->skip_instead_of_rotate = 0;
+	options->rotate_to = arg;
+	return 0;
+}
+
 static void prep_parse_options(struct diff_options *options)
 {
 	struct option parseopts[] = {
@@ -5596,6 +5612,12 @@
 			  DIFF_PICKAXE_REGEX, PARSE_OPT_NONEG),
 		OPT_FILENAME('O', NULL, &options->orderfile,
 			     N_("control the order in which files appear in the output")),
+		OPT_CALLBACK_F(0, "rotate-to", options, N_("<path>"),
+			       N_("show the change in the specified path first"),
+			       PARSE_OPT_NONEG, diff_opt_rotate_to),
+		OPT_CALLBACK_F(0, "skip-to", options, N_("<path>"),
+			       N_("skip the output to the specified path"),
+			       PARSE_OPT_NONEG, diff_opt_rotate_to),
 		OPT_CALLBACK_F(0, "find-object", options, N_("<object-id>"),
 			       N_("look for differences that change the number of occurrences of the specified object"),
 			       PARSE_OPT_NONEG, diff_opt_find_object),
@@ -6333,6 +6355,32 @@
 	}
 }
 
+static void diff_free_file(struct diff_options *options)
+{
+	if (options->close_file)
+		fclose(options->file);
+}
+
+static void diff_free_ignore_regex(struct diff_options *options)
+{
+	int i;
+
+	for (i = 0; i < options->ignore_regex_nr; i++) {
+		regfree(options->ignore_regex[i]);
+		free(options->ignore_regex[i]);
+	}
+	free(options->ignore_regex);
+}
+
+void diff_free(struct diff_options *options)
+{
+	if (options->no_free)
+		return;
+
+	diff_free_file(options);
+	diff_free_ignore_regex(options);
+}
+
 void diff_flush(struct diff_options *options)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
@@ -6396,8 +6444,7 @@
 		 * options->file to /dev/null should be safe, because we
 		 * aren't supposed to produce any output anyway.
 		 */
-		if (options->close_file)
-			fclose(options->file);
+		diff_free_file(options);
 		options->file = xfopen("/dev/null", "w");
 		options->close_file = 1;
 		options->color_moved = 0;
@@ -6430,8 +6477,7 @@
 free_queue:
 	free(q->queue);
 	DIFF_QUEUE_CLEAR(q);
-	if (options->close_file)
-		fclose(options->file);
+	diff_free(options);
 
 	/*
 	 * Report the content-level differences with HAS_CHANGES;
@@ -6666,6 +6712,8 @@
 		diffcore_pickaxe(options);
 	if (options->orderfile)
 		diffcore_order(options->orderfile);
+	if (options->rotate_to)
+		diffcore_rotate(options);
 	if (!options->found_follow)
 		/* See try_to_follow_renames() in tree-diff.c */
 		diff_resolve_rename_copy();
diff --git a/diff.h b/diff.h
index 9665e22..c8f3fae 100644
--- a/diff.h
+++ b/diff.h
@@ -49,7 +49,17 @@
  * - Once you finish feeding the pairs of files, call `diffcore_std()`.
  * This will tell the diffcore library to go ahead and do its work.
  *
- * - Calling `diff_flush()` will produce the output.
+ * - Calling `diff_flush()` will produce the output, it will call
+ *   `diff_free()` to free any resources, e.g. those allocated in
+ *   `diff_opt_parse()`.
+ *
+ * - Set `.no_free = 1` before calling `diff_flush()` to defer the
+ *   freeing of allocated memory in diff_options. This is useful when
+ *   `diff_flush()` is being called in a loop, rather than as a
+ *   one-off. When setting `.no_free = 1` you must ensure that
+ *   `diff_free()` is called at the end, either by flipping the flag
+ *   before the last `diff_flush()` call, or by flipping it before
+ *   calling `diff_free()` yourself.
  */
 
 struct combine_diff_path;
@@ -178,6 +188,7 @@
 	unsigned diff_from_contents;
 	unsigned dirty_submodules;
 	unsigned ignore_untracked_in_submodules;
+	unsigned ignore_submodule_set;
 	unsigned ignore_dirty_submodules;
 	unsigned override_submodule_config;
 	unsigned dirstat_by_line;
@@ -226,6 +237,27 @@
 struct diff_options {
 	const char *orderfile;
 
+	/*
+	 * "--rotate-to=<file>" would start showing at <file> and when
+	 * the output reaches the end, wrap around by default.
+	 * Setting skip_instead_of_rotate to true stops the output at the
+	 * end, effectively discarding the earlier part of the output
+	 * before <file>'s diff (this is used to implement the
+	 * "--skip-to=<file>" option).
+	 *
+	 * When rotate_to_strict is set, it is an error if there is no
+	 * <file> in the diff.  Otherwise, the output starts at the
+	 * path that is the same as, or first path that sorts after,
+	 * <file>.  Because it is unreasonable to require the exact
+	 * match for "git log -p --rotate-to=<file>" (i.e. not all
+	 * commit would touch that single <file>), "git log" sets it
+	 * to false.  "git diff" sets it to true to detect an error
+	 * in the command line option.
+	 */
+	const char *rotate_to;
+	int skip_instead_of_rotate;
+	int rotate_to_strict;
+
 	/**
 	 * A constant string (can and typically does contain newlines to look for
 	 * a block of text, not just a single line) to filter out the filepairs
@@ -364,6 +396,8 @@
 
 	struct repository *repo;
 	struct option *parseopts;
+
+	int no_free;
 };
 
 unsigned diff_filter_bit(char status);
@@ -558,6 +592,7 @@
 
 int diff_queue_is_empty(void);
 void diff_flush(struct diff_options*);
+void diff_free(struct diff_options*);
 void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
 
 /* diff-raw status letters */
diff --git a/diffcore-rename.c b/diffcore-rename.c
index d367a6d..4155818 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -9,63 +9,36 @@
 #include "hashmap.h"
 #include "progress.h"
 #include "promisor-remote.h"
+#include "strmap.h"
 
 /* Table of rename/copy destinations */
 
 static struct diff_rename_dst {
-	struct diff_filespec *two;
-	struct diff_filepair *pair;
+	struct diff_filepair *p;
+	struct diff_filespec *filespec_to_free;
+	int is_rename; /* false -> just a create; true -> rename or copy */
 } *rename_dst;
 static int rename_dst_nr, rename_dst_alloc;
+/* Mapping from break source pathname to break destination index */
+static struct strintmap *break_idx = NULL;
 
-static int find_rename_dst(struct diff_filespec *two)
+static struct diff_rename_dst *locate_rename_dst(struct diff_filepair *p)
 {
-	int first, last;
-
-	first = 0;
-	last = rename_dst_nr;
-	while (last > first) {
-		int next = first + ((last - first) >> 1);
-		struct diff_rename_dst *dst = &(rename_dst[next]);
-		int cmp = strcmp(two->path, dst->two->path);
-		if (!cmp)
-			return next;
-		if (cmp < 0) {
-			last = next;
-			continue;
-		}
-		first = next+1;
-	}
-	return -first - 1;
-}
-
-static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two)
-{
-	int ofs = find_rename_dst(two);
-	return ofs < 0 ? NULL : &rename_dst[ofs];
+	/* Lookup by p->ONE->path */
+	int idx = break_idx ? strintmap_get(break_idx, p->one->path) : -1;
+	return (idx == -1) ? NULL : &rename_dst[idx];
 }
 
 /*
  * Returns 0 on success, -1 if we found a duplicate.
  */
-static int add_rename_dst(struct diff_filespec *two)
+static int add_rename_dst(struct diff_filepair *p)
 {
-	int first = find_rename_dst(two);
-
-	if (first >= 0)
-		return -1;
-	first = -first - 1;
-
-	/* insert to make it at "first" */
 	ALLOC_GROW(rename_dst, rename_dst_nr + 1, rename_dst_alloc);
+	rename_dst[rename_dst_nr].p = p;
+	rename_dst[rename_dst_nr].filespec_to_free = NULL;
+	rename_dst[rename_dst_nr].is_rename = 0;
 	rename_dst_nr++;
-	if (first < rename_dst_nr)
-		MOVE_ARRAY(rename_dst + first + 1, rename_dst + first,
-			   rename_dst_nr - first - 1);
-	rename_dst[first].two = alloc_filespec(two->path);
-	fill_filespec(rename_dst[first].two, &two->oid, two->oid_valid,
-		      two->mode);
-	rename_dst[first].pair = NULL;
 	return 0;
 }
 
@@ -76,36 +49,20 @@
 } *rename_src;
 static int rename_src_nr, rename_src_alloc;
 
-static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
+static void register_rename_src(struct diff_filepair *p)
 {
-	int first, last;
-	struct diff_filespec *one = p->one;
-	unsigned short score = p->score;
-
-	first = 0;
-	last = rename_src_nr;
-	while (last > first) {
-		int next = first + ((last - first) >> 1);
-		struct diff_rename_src *src = &(rename_src[next]);
-		int cmp = strcmp(one->path, src->p->one->path);
-		if (!cmp)
-			return src;
-		if (cmp < 0) {
-			last = next;
-			continue;
+	if (p->broken_pair) {
+		if (!break_idx) {
+			break_idx = xmalloc(sizeof(*break_idx));
+			strintmap_init(break_idx, -1);
 		}
-		first = next+1;
+		strintmap_set(break_idx, p->one->path, rename_dst_nr);
 	}
 
-	/* insert to make it at "first" */
 	ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc);
+	rename_src[rename_src_nr].p = p;
+	rename_src[rename_src_nr].score = p->score;
 	rename_src_nr++;
-	if (first < rename_src_nr)
-		MOVE_ARRAY(rename_src + first + 1, rename_src + first,
-			   rename_src_nr - first - 1);
-	rename_src[first].p = p;
-	rename_src[first].score = score;
-	return &(rename_src[first]);
 }
 
 static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
@@ -141,14 +98,14 @@
 	struct oid_array to_fetch = OID_ARRAY_INIT;
 
 	for (i = 0; i < rename_dst_nr; i++) {
-		if (rename_dst[i].pair)
+		if (rename_dst[i].p->renamed_pair)
 			/*
 			 * The loop in diffcore_rename() will not need these
 			 * blobs, so skip prefetching.
 			 */
 			continue; /* already found exact match */
 		diff_add_if_missing(options->repo, &to_fetch,
-				    rename_dst[i].two);
+				    rename_dst[i].p->two);
 	}
 	for (i = 0; i < rename_src_nr; i++) {
 		if (options->skip_unmodified &&
@@ -258,26 +215,24 @@
 
 static void record_rename_pair(int dst_index, int src_index, int score)
 {
-	struct diff_filespec *src, *dst;
-	struct diff_filepair *dp;
+	struct diff_filepair *src = rename_src[src_index].p;
+	struct diff_filepair *dst = rename_dst[dst_index].p;
 
-	if (rename_dst[dst_index].pair)
+	if (dst->renamed_pair)
 		die("internal error: dst already matched.");
 
-	src = rename_src[src_index].p->one;
-	src->rename_used++;
-	src->count++;
+	src->one->rename_used++;
+	src->one->count++;
 
-	dst = rename_dst[dst_index].two;
-	dst->count++;
+	rename_dst[dst_index].filespec_to_free = dst->one;
+	rename_dst[dst_index].is_rename = 1;
 
-	dp = diff_queue(NULL, src, dst);
-	dp->renamed_pair = 1;
-	if (!strcmp(src->path, dst->path))
-		dp->score = rename_src[src_index].score;
+	dst->one = src->one;
+	dst->renamed_pair = 1;
+	if (!strcmp(dst->one->path, dst->two->path))
+		dst->score = rename_src[src_index].score;
 	else
-		dp->score = score;
-	rename_dst[dst_index].pair = dp;
+		dst->score = score;
 }
 
 /*
@@ -323,7 +278,7 @@
 				struct diff_options *options)
 {
 	int renames = 0;
-	struct diff_filespec *target = rename_dst[dst_index].two;
+	struct diff_filespec *target = rename_dst[dst_index].p->two;
 	struct file_similarity *p, *best = NULL;
 	int i = 100, best_score = -1;
 	unsigned int hash = hash_filespec(options->repo, target);
@@ -412,6 +367,144 @@
 	return renames;
 }
 
+static const char *get_basename(const char *filename)
+{
+	/*
+	 * gitbasename() has to worry about special drives, multiple
+	 * directory separator characters, trailing slashes, NULL or
+	 * empty strings, etc.  We only work on filenames as stored in
+	 * git, and thus get to ignore all those complications.
+	 */
+	const char *base = strrchr(filename, '/');
+	return base ? base + 1 : filename;
+}
+
+static int find_basename_matches(struct diff_options *options,
+				 int minimum_score)
+{
+	/*
+	 * When I checked in early 2020, over 76% of file renames in linux
+	 * just moved files to a different directory but kept the same
+	 * basename.  gcc did that with over 64% of renames, gecko did it
+	 * with over 79%, and WebKit did it with over 89%.
+	 *
+	 * Therefore we can bypass the normal exhaustive NxM matrix
+	 * comparison of similarities between all potential rename sources
+	 * and destinations by instead using file basename as a hint (i.e.
+	 * the portion of the filename after the last '/'), checking for
+	 * similarity between files with the same basename, and if we find
+	 * a pair that are sufficiently similar, record the rename pair and
+	 * exclude those two from the NxM matrix.
+	 *
+	 * This *might* cause us to find a less than optimal pairing (if
+	 * there is another file that we are even more similar to but has a
+	 * different basename).  Given the huge performance advantage
+	 * basename matching provides, and given the frequency with which
+	 * people use the same basename in real world projects, that's a
+	 * trade-off we are willing to accept when doing just rename
+	 * detection.
+	 *
+	 * If someone wants copy detection that implies they are willing to
+	 * spend more cycles to find similarities between files, so it may
+	 * be less likely that this heuristic is wanted.  If someone is
+	 * doing break detection, that means they do not want filename
+	 * similarity to imply any form of content similiarity, and thus
+	 * this heuristic would definitely be incompatible.
+	 */
+
+	int i, renames = 0;
+	struct strintmap sources;
+	struct strintmap dests;
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
+
+	/*
+	 * The prefeteching stuff wants to know if it can skip prefetching
+	 * blobs that are unmodified...and will then do a little extra work
+	 * to verify that the oids are indeed different before prefetching.
+	 * Unmodified blobs are only relevant when doing copy detection;
+	 * when limiting to rename detection, diffcore_rename[_extended]()
+	 * will never be called with unmodified source paths fed to us, so
+	 * the extra work necessary to check if rename_src entries are
+	 * unmodified would be a small waste.
+	 */
+	int skip_unmodified = 0;
+
+	/*
+	 * Create maps of basename -> fullname(s) for remaining sources and
+	 * dests.
+	 */
+	strintmap_init_with_options(&sources, -1, NULL, 0);
+	strintmap_init_with_options(&dests, -1, NULL, 0);
+	for (i = 0; i < rename_src_nr; ++i) {
+		char *filename = rename_src[i].p->one->path;
+		const char *base;
+
+		/* exact renames removed in remove_unneeded_paths_from_src() */
+		assert(!rename_src[i].p->one->rename_used);
+
+		/* Record index within rename_src (i) if basename is unique */
+		base = get_basename(filename);
+		if (strintmap_contains(&sources, base))
+			strintmap_set(&sources, base, -1);
+		else
+			strintmap_set(&sources, base, i);
+	}
+	for (i = 0; i < rename_dst_nr; ++i) {
+		char *filename = rename_dst[i].p->two->path;
+		const char *base;
+
+		if (rename_dst[i].is_rename)
+			continue; /* involved in exact match already. */
+
+		/* Record index within rename_dst (i) if basename is unique */
+		base = get_basename(filename);
+		if (strintmap_contains(&dests, base))
+			strintmap_set(&dests, base, -1);
+		else
+			strintmap_set(&dests, base, i);
+	}
+
+	/* Now look for basename matchups and do similarity estimation */
+	strintmap_for_each_entry(&sources, &iter, entry) {
+		const char *base = entry->key;
+		intptr_t src_index = (intptr_t)entry->value;
+		intptr_t dst_index;
+		if (src_index == -1)
+			continue;
+
+		if (0 <= (dst_index = strintmap_get(&dests, base))) {
+			struct diff_filespec *one, *two;
+			int score;
+
+			/* Estimate the similarity */
+			one = rename_src[src_index].p->one;
+			two = rename_dst[dst_index].p->two;
+			score = estimate_similarity(options->repo, one, two,
+						    minimum_score, skip_unmodified);
+
+			/* If sufficiently similar, record as rename pair */
+			if (score < minimum_score)
+				continue;
+			record_rename_pair(dst_index, src_index, score);
+			renames++;
+
+			/*
+			 * Found a rename so don't need text anymore; if we
+			 * didn't find a rename, the filespec_blob would get
+			 * re-used when doing the matrix of comparisons.
+			 */
+			diff_free_filespec_blob(one);
+			diff_free_filespec_blob(two);
+		}
+	}
+
+	strintmap_clear(&sources);
+	strintmap_clear(&dests);
+
+	return renames;
+}
+
 #define NUM_CANDIDATE_PER_DST 4
 static void record_if_better(struct diff_score m[], struct diff_score *o)
 {
@@ -434,12 +527,11 @@
  * 1 if we need to disable inexact rename detection;
  * 2 if we would be under the limit if we were given -C instead of -C -C.
  */
-static int too_many_rename_candidates(int num_create,
+static int too_many_rename_candidates(int num_destinations, int num_sources,
 				      struct diff_options *options)
 {
 	int rename_limit = options->rename_limit;
-	int num_src = rename_src_nr;
-	int i;
+	int i, limited_sources;
 
 	options->needed_rename_limit = 0;
 
@@ -447,31 +539,34 @@
 	 * This basically does a test for the rename matrix not
 	 * growing larger than a "rename_limit" square matrix, ie:
 	 *
-	 *    num_create * num_src > rename_limit * rename_limit
+	 *    num_destinations * num_sources > rename_limit * rename_limit
+	 *
+	 * We use st_mult() to check overflow conditions; in the
+	 * exceptional circumstance that size_t isn't large enough to hold
+	 * the multiplication, the system won't be able to allocate enough
+	 * memory for the matrix anyway.
 	 */
 	if (rename_limit <= 0)
 		rename_limit = 32767;
-	if ((num_create <= rename_limit || num_src <= rename_limit) &&
-	    ((uint64_t)num_create * (uint64_t)num_src
-	     <= (uint64_t)rename_limit * (uint64_t)rename_limit))
+	if (st_mult(num_destinations, num_sources)
+	    <= st_mult(rename_limit, rename_limit))
 		return 0;
 
 	options->needed_rename_limit =
-		num_src > num_create ? num_src : num_create;
+		num_sources > num_destinations ? num_sources : num_destinations;
 
 	/* Are we running under -C -C? */
 	if (!options->flags.find_copies_harder)
 		return 1;
 
 	/* Would we bust the limit if we were running under -C? */
-	for (num_src = i = 0; i < rename_src_nr; i++) {
+	for (limited_sources = i = 0; i < num_sources; i++) {
 		if (diff_unmodified_pair(rename_src[i].p))
 			continue;
-		num_src++;
+		limited_sources++;
 	}
-	if ((num_create <= rename_limit || num_src <= rename_limit) &&
-	    ((uint64_t)num_create * (uint64_t)num_src
-	     <= (uint64_t)rename_limit * (uint64_t)rename_limit))
+	if (st_mult(num_destinations, limited_sources)
+	    <= st_mult(rename_limit, rename_limit))
 		return 2;
 	return 1;
 }
@@ -487,7 +582,7 @@
 		    (mx[i].score < minimum_score))
 			break; /* there is no more usable pair. */
 		dst = &rename_dst[mx[i].dst];
-		if (dst->pair)
+		if (dst->is_rename)
 			continue; /* already done, either exact or fuzzy. */
 		if (!copies && rename_src[mx[i].src].p->one->rename_used)
 			continue;
@@ -497,6 +592,54 @@
 	return count;
 }
 
+static void remove_unneeded_paths_from_src(int detecting_copies)
+{
+	int i, new_num_src;
+
+	if (detecting_copies)
+		return; /* nothing to remove */
+	if (break_idx)
+		return; /* culling incompatible with break detection */
+
+	/*
+	 * Note on reasons why we cull unneeded sources but not destinations:
+	 *   1) Pairings are stored in rename_dst (not rename_src), which we
+	 *      need to keep around.  So, we just can't cull rename_dst even
+	 *      if we wanted to.  But doing so wouldn't help because...
+	 *
+	 *   2) There is a matrix pairwise comparison that follows the
+	 *      "Performing inexact rename detection" progress message.
+	 *      Iterating over the destinations is done in the outer loop,
+	 *      hence we only iterate over each of those once and we can
+	 *      easily skip the outer loop early if the destination isn't
+	 *      relevant.  That's only one check per destination path to
+	 *      skip.
+	 *
+	 *      By contrast, the sources are iterated in the inner loop; if
+	 *      we check whether a source can be skipped, then we'll be
+	 *      checking it N separate times, once for each destination.
+	 *      We don't want to have to iterate over known-not-needed
+	 *      sources N times each, so avoid that by removing the sources
+	 *      from rename_src here.
+	 */
+	for (i = 0, new_num_src = 0; i < rename_src_nr; i++) {
+		/*
+		 * renames are stored in rename_dst, so if a rename has
+		 * already been detected using this source, we can just
+		 * remove the source knowing rename_dst has its info.
+		 */
+		if (rename_src[i].p->one->rename_used)
+			continue;
+
+		if (new_num_src < i)
+			memcpy(&rename_src[new_num_src], &rename_src[i],
+			       sizeof(struct diff_rename_src));
+		new_num_src++;
+	}
+
+	rename_src_nr = new_num_src;
+}
+
 void diffcore_rename(struct diff_options *options)
 {
 	int detect_rename = options->detect_rename;
@@ -505,9 +648,12 @@
 	struct diff_queue_struct outq;
 	struct diff_score *mx;
 	int i, j, rename_count, skip_unmodified = 0;
-	int num_create, dst_cnt;
+	int num_destinations, dst_cnt;
+	int num_sources, want_copies;
 	struct progress *progress = NULL;
 
+	trace2_region_enter("diff", "setup", options->repo);
+	want_copies = (detect_rename == DIFF_DETECT_COPY);
 	if (!minimum_score)
 		minimum_score = DEFAULT_RENAME_SCORE;
 
@@ -522,7 +668,7 @@
 			else if (!options->flags.rename_empty &&
 				 is_empty_blob_oid(&p->two->oid))
 				continue;
-			else if (add_rename_dst(p->two) < 0) {
+			else if (add_rename_dst(p) < 0) {
 				warning("skipping rename detection, detected"
 					" duplicate destination '%s'",
 					p->two->path);
@@ -544,7 +690,7 @@
 				p->one->rename_used++;
 			register_rename_src(p);
 		}
-		else if (detect_rename == DIFF_DETECT_COPY) {
+		else if (want_copies) {
 			/*
 			 * Increment the "rename_used" score by
 			 * one, to indicate ourselves as a user.
@@ -553,30 +699,77 @@
 			register_rename_src(p);
 		}
 	}
+	trace2_region_leave("diff", "setup", options->repo);
 	if (rename_dst_nr == 0 || rename_src_nr == 0)
 		goto cleanup; /* nothing to do */
 
+	trace2_region_enter("diff", "exact renames", options->repo);
 	/*
 	 * We really want to cull the candidates list early
 	 * with cheap tests in order to avoid doing deltas.
 	 */
 	rename_count = find_exact_renames(options);
+	trace2_region_leave("diff", "exact renames", options->repo);
 
 	/* Did we only want exact renames? */
 	if (minimum_score == MAX_SCORE)
 		goto cleanup;
 
-	/*
-	 * Calculate how many renames are left (but all the source
-	 * files still remain as options for rename/copies!)
-	 */
-	num_create = (rename_dst_nr - rename_count);
+	num_sources = rename_src_nr;
+
+	if (want_copies || break_idx) {
+		/*
+		 * Cull sources:
+		 *   - remove ones corresponding to exact renames
+		 */
+		trace2_region_enter("diff", "cull after exact", options->repo);
+		remove_unneeded_paths_from_src(want_copies);
+		trace2_region_leave("diff", "cull after exact", options->repo);
+	} else {
+		/* Determine minimum score to match basenames */
+		double factor = 0.5;
+		char *basename_factor = getenv("GIT_BASENAME_FACTOR");
+		int min_basename_score;
+
+		if (basename_factor)
+			factor = strtol(basename_factor, NULL, 10)/100.0;
+		assert(factor >= 0.0 && factor <= 1.0);
+		min_basename_score = minimum_score +
+			(int)(factor * (MAX_SCORE - minimum_score));
+
+		/*
+		 * Cull sources:
+		 *   - remove ones involved in renames (found via exact match)
+		 */
+		trace2_region_enter("diff", "cull after exact", options->repo);
+		remove_unneeded_paths_from_src(want_copies);
+		trace2_region_leave("diff", "cull after exact", options->repo);
+
+		/* Utilize file basenames to quickly find renames. */
+		trace2_region_enter("diff", "basename matches", options->repo);
+		rename_count += find_basename_matches(options,
+						      min_basename_score);
+		trace2_region_leave("diff", "basename matches", options->repo);
+
+		/*
+		 * Cull sources, again:
+		 *   - remove ones involved in renames (found via basenames)
+		 */
+		trace2_region_enter("diff", "cull basename", options->repo);
+		remove_unneeded_paths_from_src(want_copies);
+		trace2_region_leave("diff", "cull basename", options->repo);
+	}
+
+	/* Calculate how many rename destinations are left */
+	num_destinations = (rename_dst_nr - rename_count);
+	num_sources = rename_src_nr; /* rename_src_nr reflects lower number */
 
 	/* All done? */
-	if (!num_create)
+	if (!num_destinations || !num_sources)
 		goto cleanup;
 
-	switch (too_many_rename_candidates(num_create, options)) {
+	switch (too_many_rename_candidates(num_destinations, num_sources,
+					   options)) {
 	case 1:
 		goto cleanup;
 	case 2:
@@ -587,19 +780,21 @@
 		break;
 	}
 
+	trace2_region_enter("diff", "inexact renames", options->repo);
 	if (options->show_rename_progress) {
 		progress = start_delayed_progress(
 				_("Performing inexact rename detection"),
-				(uint64_t)rename_dst_nr * (uint64_t)rename_src_nr);
+				(uint64_t)num_destinations * (uint64_t)num_sources);
 	}
 
-	mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_create), sizeof(*mx));
+	mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_destinations),
+		     sizeof(*mx));
 	for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
-		struct diff_filespec *two = rename_dst[i].two;
+		struct diff_filespec *two = rename_dst[i].p->two;
 		struct diff_score *m;
 
-		if (rename_dst[i].pair)
-			continue; /* dealt with exact match already. */
+		if (rename_dst[i].is_rename)
+			continue; /* exact or basename match already handled */
 
 		m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST];
 		for (j = 0; j < NUM_CANDIDATE_PER_DST; j++)
@@ -609,6 +804,8 @@
 			struct diff_filespec *one = rename_src[j].p->one;
 			struct diff_score this_src;
 
+			assert(!one->rename_used || want_copies || break_idx);
+
 			if (skip_unmodified &&
 			    diff_unmodified_pair(rename_src[j].p))
 				continue;
@@ -629,7 +826,8 @@
 			diff_free_filespec_blob(two);
 		}
 		dst_cnt++;
-		display_progress(progress, (uint64_t)(i+1)*(uint64_t)rename_src_nr);
+		display_progress(progress,
+				 (uint64_t)dst_cnt * (uint64_t)num_sources);
 	}
 	stop_progress(&progress);
 
@@ -637,14 +835,16 @@
 	STABLE_QSORT(mx, dst_cnt * NUM_CANDIDATE_PER_DST, score_compare);
 
 	rename_count += find_renames(mx, dst_cnt, minimum_score, 0);
-	if (detect_rename == DIFF_DETECT_COPY)
+	if (want_copies)
 		rename_count += find_renames(mx, dst_cnt, minimum_score, 1);
 	free(mx);
+	trace2_region_leave("diff", "inexact renames", options->repo);
 
  cleanup:
 	/* At this point, we have found some renames and copies and they
 	 * are recorded in rename_dst.  The original list is still in *q.
 	 */
+	trace2_region_enter("diff", "write back to queue", options->repo);
 	DIFF_QUEUE_CLEAR(&outq);
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
@@ -654,22 +854,8 @@
 			diff_q(&outq, p);
 		}
 		else if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
-			/*
-			 * Creation
-			 *
-			 * We would output this create record if it has
-			 * not been turned into a rename/copy already.
-			 */
-			struct diff_rename_dst *dst = locate_rename_dst(p->two);
-			if (dst && dst->pair) {
-				diff_q(&outq, dst->pair);
-				pair_to_free = p;
-			}
-			else
-				/* no matching rename/copy source, so
-				 * record this as a creation.
-				 */
-				diff_q(&outq, p);
+			/* Creation */
+			diff_q(&outq, p);
 		}
 		else if (DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) {
 			/*
@@ -690,8 +876,10 @@
 			 */
 			if (DIFF_PAIR_BROKEN(p)) {
 				/* broken delete */
-				struct diff_rename_dst *dst = locate_rename_dst(p->one);
-				if (dst && dst->pair)
+				struct diff_rename_dst *dst = locate_rename_dst(p);
+				if (!dst)
+					BUG("tracking failed somehow; failed to find associated dst for broken pair");
+				if (dst->is_rename)
 					/* counterpart is now rename/copy */
 					pair_to_free = p;
 			}
@@ -701,16 +889,14 @@
 					pair_to_free = p;
 			}
 
-			if (pair_to_free)
-				;
-			else
+			if (!pair_to_free)
 				diff_q(&outq, p);
 		}
 		else if (!diff_unmodified_pair(p))
 			/* all the usual ones need to be kept */
 			diff_q(&outq, p);
 		else
-			/* no need to keep unmodified pairs */
+			/* no need to keep unmodified pairs; FIXME: remove earlier? */
 			pair_to_free = p;
 
 		if (pair_to_free)
@@ -723,11 +909,17 @@
 	diff_debug_queue("done collapsing", q);
 
 	for (i = 0; i < rename_dst_nr; i++)
-		free_filespec(rename_dst[i].two);
+		if (rename_dst[i].filespec_to_free)
+			free_filespec(rename_dst[i].filespec_to_free);
 
 	FREE_AND_NULL(rename_dst);
 	rename_dst_nr = rename_dst_alloc = 0;
 	FREE_AND_NULL(rename_src);
 	rename_src_nr = rename_src_alloc = 0;
+	if (break_idx) {
+		strintmap_clear(break_idx);
+		FREE_AND_NULL(break_idx);
+	}
+	trace2_region_leave("diff", "write back to queue", options->repo);
 	return;
 }
diff --git a/diffcore-rotate.c b/diffcore-rotate.c
new file mode 100644
index 0000000..445f060
--- /dev/null
+++ b/diffcore-rotate.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, Google LLC.
+ * Based on diffcore-order.c, which is Copyright (C) 2005, Junio C Hamano
+ */
+#include "cache.h"
+#include "diff.h"
+#include "diffcore.h"
+
+void diffcore_rotate(struct diff_options *opt)
+{
+	struct diff_queue_struct *q = &diff_queued_diff;
+	struct diff_queue_struct outq;
+	int rotate_to, i;
+
+	if (!q->nr)
+		return;
+
+	for (i = 0; i < q->nr; i++) {
+		int cmp = strcmp(opt->rotate_to, q->queue[i]->two->path);
+		if (!cmp)
+			break; /* exact match */
+		if (!opt->rotate_to_strict && cmp < 0)
+			break; /* q->queue[i] is now past the target pathname */
+	}
+
+	if (q->nr <= i) {
+		/* we did not find the specified path */
+		if (opt->rotate_to_strict)
+			die(_("No such path '%s' in the diff"), opt->rotate_to);
+		return;
+	}
+
+	DIFF_QUEUE_CLEAR(&outq);
+	rotate_to = i;
+
+	for (i = rotate_to; i < q->nr; i++)
+		diff_q(&outq, q->queue[i]);
+	for (i = 0; i < rotate_to; i++) {
+		if (opt->skip_instead_of_rotate)
+			diff_free_filepair(q->queue[i]);
+		else
+			diff_q(&outq, q->queue[i]);
+	}
+	free(q->queue);
+	*q = outq;
+}
diff --git a/diffcore.h b/diffcore.h
index d2a63c5..c1592bc 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -164,6 +164,7 @@
 void diffcore_merge_broken(void);
 void diffcore_pickaxe(struct diff_options *);
 void diffcore_order(const char *orderfile);
+void diffcore_rotate(struct diff_options *);
 
 /* low-level interface to diffcore_order */
 struct obj_order {
diff --git a/dir.c b/dir.c
index d637461..fd8aa7c 100644
--- a/dir.c
+++ b/dir.c
@@ -2730,11 +2730,8 @@
 		return NULL;
 	}
 
-	if (!dir->untracked->root) {
-		const int len = sizeof(*dir->untracked->root);
-		dir->untracked->root = xmalloc(len);
-		memset(dir->untracked->root, 0, len);
-	}
+	if (!dir->untracked->root)
+		FLEX_ALLOC_STR(dir->untracked->root, name, "");
 
 	/* Validate $GIT_DIR/info/exclude and core.excludesfile */
 	root = dir->untracked->root;
@@ -2998,6 +2995,23 @@
 	}
 }
 
+char *get_sparse_checkout_filename(void)
+{
+	return git_pathdup("info/sparse-checkout");
+}
+
+int get_sparse_checkout_patterns(struct pattern_list *pl)
+{
+	int res;
+	char *sparse_filename = get_sparse_checkout_filename();
+
+	pl->use_cone_patterns = core_sparse_checkout_cone;
+	res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL);
+
+	free(sparse_filename);
+	return res;
+}
+
 int remove_path(const char *name)
 {
 	char *slash;
diff --git a/dir.h b/dir.h
index a3c40de..facfae4 100644
--- a/dir.h
+++ b/dir.h
@@ -448,6 +448,8 @@
 
 void setup_standard_excludes(struct dir_struct *dir);
 
+char *get_sparse_checkout_filename(void);
+int get_sparse_checkout_patterns(struct pattern_list *pl);
 
 /* Constants for remove_dir_recursively: */
 
diff --git a/entry.c b/entry.c
index a0532f1..7b9f437 100644
--- a/entry.c
+++ b/entry.c
@@ -282,7 +282,7 @@
 		new_blob = read_blob_entry(ce, &size);
 		if (!new_blob)
 			return error("unable to read sha1 file of %s (%s)",
-				     path, oid_to_hex(&ce->oid));
+				     ce->name, oid_to_hex(&ce->oid));
 
 		/*
 		 * We can't make a real symlink; write out a regular file entry
@@ -309,7 +309,7 @@
 			new_blob = read_blob_entry(ce, &size);
 			if (!new_blob)
 				return error("unable to read sha1 file of %s (%s)",
-					     path, oid_to_hex(&ce->oid));
+					     ce->name, oid_to_hex(&ce->oid));
 		}
 
 		/*
@@ -354,7 +354,7 @@
 
 	case S_IFGITLINK:
 		if (to_tempfile)
-			return error("cannot create temporary submodule %s", path);
+			return error("cannot create temporary submodule %s", ce->name);
 		if (mkdir(path, 0777) < 0)
 			return error("cannot create submodule directory %s", path);
 		sub = submodule_from_ce(ce);
@@ -365,7 +365,7 @@
 		break;
 
 	default:
-		return error("unknown file mode for %s in index", path);
+		return error("unknown file mode for %s in index", ce->name);
 	}
 
 finish:
diff --git a/environment.c b/environment.c
index bb518c6..2f27008 100644
--- a/environment.c
+++ b/environment.c
@@ -9,6 +9,7 @@
  */
 #include "cache.h"
 #include "branch.h"
+#include "environment.h"
 #include "repository.h"
 #include "config.h"
 #include "refs.h"
@@ -116,6 +117,7 @@
 	ALTERNATE_DB_ENVIRONMENT,
 	CONFIG_ENVIRONMENT,
 	CONFIG_DATA_ENVIRONMENT,
+	CONFIG_COUNT_ENVIRONMENT,
 	DB_ENVIRONMENT,
 	GIT_DIR_ENVIRONMENT,
 	GIT_WORK_TREE_ENVIRONMENT,
@@ -152,11 +154,7 @@
 	return strbuf_detach(&buf, NULL);
 }
 
-/*
- * Wrapper of getenv() that returns a strdup value. This value is kept
- * in argv to be freed later.
- */
-static const char *getenv_safe(struct strvec *argv, const char *name)
+const char *getenv_safe(struct strvec *argv, const char *name)
 {
 	const char *value = getenv(name);
 
diff --git a/environment.h b/environment.h
new file mode 100644
index 0000000..d438b5c
--- /dev/null
+++ b/environment.h
@@ -0,0 +1,12 @@
+#ifndef ENVIRONMENT_H
+#define ENVIRONMENT_H
+
+#include "strvec.h"
+
+/*
+ * Wrapper of getenv() that returns a strdup value. This value is kept
+ * in argv to be freed later.
+ */
+const char *getenv_safe(struct strvec *argv, const char *name);
+
+#endif
diff --git a/ewah/bitmap.c b/ewah/bitmap.c
index d8cec58..0d31cdc 100644
--- a/ewah/bitmap.c
+++ b/ewah/bitmap.c
@@ -35,18 +35,26 @@
 	return bitmap_word_alloc(32);
 }
 
+struct bitmap *bitmap_dup(const struct bitmap *src)
+{
+	struct bitmap *dst = bitmap_word_alloc(src->word_alloc);
+	COPY_ARRAY(dst->words, src->words, src->word_alloc);
+	return dst;
+}
+
+static void bitmap_grow(struct bitmap *self, size_t word_alloc)
+{
+	size_t old_size = self->word_alloc;
+	ALLOC_GROW(self->words, word_alloc, self->word_alloc);
+	memset(self->words + old_size, 0x0,
+	       (self->word_alloc - old_size) * sizeof(eword_t));
+}
+
 void bitmap_set(struct bitmap *self, size_t pos)
 {
 	size_t block = EWAH_BLOCK(pos);
 
-	if (block >= self->word_alloc) {
-		size_t old_size = self->word_alloc;
-		self->word_alloc = block ? block * 2 : 1;
-		REALLOC_ARRAY(self->words, self->word_alloc);
-		memset(self->words + old_size, 0x0,
-			(self->word_alloc - old_size) * sizeof(eword_t));
-	}
-
+	bitmap_grow(self, block + 1);
 	self->words[block] |= EWAH_MASK(pos);
 }
 
@@ -121,6 +129,15 @@
 		self->words[i] &= ~other->words[i];
 }
 
+void bitmap_or(struct bitmap *self, const struct bitmap *other)
+{
+	size_t i;
+
+	bitmap_grow(self, other->word_alloc);
+	for (i = 0; i < other->word_alloc; i++)
+		self->words[i] |= other->words[i];
+}
+
 void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
 {
 	size_t original_size = self->word_alloc;
@@ -178,6 +195,27 @@
 	return 1;
 }
 
+int bitmap_is_subset(struct bitmap *self, struct bitmap *other)
+{
+	size_t common_size, i;
+
+	if (self->word_alloc < other->word_alloc)
+		common_size = self->word_alloc;
+	else {
+		common_size = other->word_alloc;
+		for (i = common_size; i < self->word_alloc; i++) {
+			if (self->words[i])
+				return 1;
+		}
+	}
+
+	for (i = 0; i < common_size; i++) {
+		if (self->words[i] & ~other->words[i])
+			return 1;
+	}
+	return 0;
+}
+
 void bitmap_reset(struct bitmap *bitmap)
 {
 	memset(bitmap->words, 0x0, bitmap->word_alloc * sizeof(eword_t));
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
index d59b1af..2a8c7c5 100644
--- a/ewah/ewah_bitmap.c
+++ b/ewah/ewah_bitmap.c
@@ -19,6 +19,7 @@
 #include "git-compat-util.h"
 #include "ewok.h"
 #include "ewok_rlw.h"
+#include "cache.h"
 
 static inline size_t min_size(size_t a, size_t b)
 {
@@ -33,20 +34,13 @@
 static inline void buffer_grow(struct ewah_bitmap *self, size_t new_size)
 {
 	size_t rlw_offset = (uint8_t *)self->rlw - (uint8_t *)self->buffer;
-
-	if (self->alloc_size >= new_size)
-		return;
-
-	self->alloc_size = new_size;
-	REALLOC_ARRAY(self->buffer, self->alloc_size);
+	ALLOC_GROW(self->buffer, new_size, self->alloc_size);
 	self->rlw = self->buffer + (rlw_offset / sizeof(eword_t));
 }
 
 static inline void buffer_push(struct ewah_bitmap *self, eword_t value)
 {
-	if (self->buffer_size + 1 >= self->alloc_size)
-		buffer_grow(self, self->buffer_size * 3 / 2);
-
+	buffer_grow(self, self->buffer_size + 1);
 	self->buffer[self->buffer_size++] = value;
 }
 
@@ -137,8 +131,7 @@
 
 		rlw_set_literal_words(self->rlw, literals + can_add);
 
-		if (self->buffer_size + can_add >= self->alloc_size)
-			buffer_grow(self, (self->buffer_size + can_add) * 3 / 2);
+		buffer_grow(self, self->buffer_size + can_add);
 
 		if (negate) {
 			size_t i;
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 011852b..6692096 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -173,13 +173,14 @@
 
 struct bitmap *bitmap_new(void);
 struct bitmap *bitmap_word_alloc(size_t word_alloc);
+struct bitmap *bitmap_dup(const struct bitmap *src);
 void bitmap_set(struct bitmap *self, size_t pos);
 void bitmap_unset(struct bitmap *self, size_t pos);
 int bitmap_get(struct bitmap *self, size_t pos);
 void bitmap_reset(struct bitmap *self);
 void bitmap_free(struct bitmap *self);
 int bitmap_equals(struct bitmap *self, struct bitmap *other);
-int bitmap_is_subset(struct bitmap *self, struct bitmap *super);
+int bitmap_is_subset(struct bitmap *self, struct bitmap *other);
 
 struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);
 struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);
diff --git a/fetch-pack.c b/fetch-pack.c
index 876f90c..6a61a46 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -772,13 +772,11 @@
 	return ret;
 }
 
-static void write_promisor_file(const char *keep_name,
-				struct ref **sought, int nr_sought)
+static void create_promisor_file(const char *keep_name,
+				 struct ref **sought, int nr_sought)
 {
 	struct strbuf promisor_name = STRBUF_INIT;
 	int suffix_stripped;
-	FILE *output;
-	int i;
 
 	strbuf_addstr(&promisor_name, keep_name);
 	suffix_stripped = strbuf_strip_suffix(&promisor_name, ".keep");
@@ -787,23 +785,41 @@
 		    keep_name);
 	strbuf_addstr(&promisor_name, ".promisor");
 
-	output = xfopen(promisor_name.buf, "w");
-	for (i = 0; i < nr_sought; i++)
-		fprintf(output, "%s %s\n", oid_to_hex(&sought[i]->old_oid),
-			sought[i]->name);
-	fclose(output);
+	write_promisor_file(promisor_name.buf, sought, nr_sought);
 
 	strbuf_release(&promisor_name);
 }
 
+static void parse_gitmodules_oids(int fd, struct oidset *gitmodules_oids)
+{
+	int len = the_hash_algo->hexsz + 1; /* hash + NL */
+
+	do {
+		char hex_hash[GIT_MAX_HEXSZ + 1];
+		int read_len = read_in_full(fd, hex_hash, len);
+		struct object_id oid;
+		const char *end;
+
+		if (!read_len)
+			return;
+		if (read_len != len)
+			die("invalid length read %d", read_len);
+		if (parse_oid_hex(hex_hash, &oid, &end) || *end != '\n')
+			die("invalid hash");
+		oidset_insert(gitmodules_oids, &oid);
+	} while (1);
+}
+
 /*
- * Pass 1 as "only_packfile" if the pack received is the only pack in this
- * fetch request (that is, if there were no packfile URIs provided).
+ * If packfile URIs were provided, pass a non-NULL pointer to index_pack_args.
+ * The strings to pass as the --index-pack-arg arguments to http-fetch will be
+ * stored there. (It must be freed by the caller.)
  */
 static int get_pack(struct fetch_pack_args *args,
 		    int xd[2], struct string_list *pack_lockfiles,
-		    int only_packfile,
-		    struct ref **sought, int nr_sought)
+		    struct strvec *index_pack_args,
+		    struct ref **sought, int nr_sought,
+		    struct oidset *gitmodules_oids)
 {
 	struct async demux;
 	int do_keep = args->keep_pack;
@@ -811,6 +827,7 @@
 	struct pack_header header;
 	int pass_header = 0;
 	struct child_process cmd = CHILD_PROCESS_INIT;
+	int fsck_objects = 0;
 	int ret;
 
 	memset(&demux, 0, sizeof(demux));
@@ -829,7 +846,7 @@
 	else
 		demux.out = xd[0];
 
-	if (!args->keep_pack && unpack_limit) {
+	if (!args->keep_pack && unpack_limit && !index_pack_args) {
 
 		if (read_pack_header(demux.out, &header))
 			die(_("protocol error: bad pack header"));
@@ -845,8 +862,15 @@
 		strvec_push(&cmd.args, alternate_shallow_file);
 	}
 
-	if (do_keep || args->from_promisor) {
-		if (pack_lockfiles)
+	if (fetch_fsck_objects >= 0
+	    ? fetch_fsck_objects
+	    : transfer_fsck_objects >= 0
+	    ? transfer_fsck_objects
+	    : 0)
+		fsck_objects = 1;
+
+	if (do_keep || args->from_promisor || index_pack_args || fsck_objects) {
+		if (pack_lockfiles || fsck_objects)
 			cmd.out = -1;
 		cmd_name = "index-pack";
 		strvec_push(&cmd.args, cmd_name);
@@ -855,7 +879,7 @@
 			strvec_push(&cmd.args, "-v");
 		if (args->use_thin_pack)
 			strvec_push(&cmd.args, "--fix-thin");
-		if (do_keep && (args->lock_pack || unpack_limit)) {
+		if ((do_keep || index_pack_args) && (args->lock_pack || unpack_limit)) {
 			char hostname[HOST_NAME_MAX + 1];
 			if (xgethostname(hostname, sizeof(hostname)))
 				xsnprintf(hostname, sizeof(hostname), "localhost");
@@ -863,7 +887,7 @@
 				     "--keep=fetch-pack %"PRIuMAX " on %s",
 				     (uintmax_t)getpid(), hostname);
 		}
-		if (only_packfile && args->check_self_contained_and_connected)
+		if (!index_pack_args && args->check_self_contained_and_connected)
 			strvec_push(&cmd.args, "--check-self-contained-and-connected");
 		else
 			/*
@@ -875,7 +899,7 @@
 
 		if (args->from_promisor)
 			/*
-			 * write_promisor_file() may be called afterwards but
+			 * create_promisor_file() may be called afterwards but
 			 * we still need index-pack to know that this is a
 			 * promisor pack. For example, if transfer.fsckobjects
 			 * is true, index-pack needs to know that .gitmodules
@@ -896,12 +920,8 @@
 		strvec_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
 			     ntohl(header.hdr_version),
 				 ntohl(header.hdr_entries));
-	if (fetch_fsck_objects >= 0
-	    ? fetch_fsck_objects
-	    : transfer_fsck_objects >= 0
-	    ? transfer_fsck_objects
-	    : 0) {
-		if (args->from_promisor || !only_packfile)
+	if (fsck_objects) {
+		if (args->from_promisor || index_pack_args)
 			/*
 			 * We cannot use --strict in index-pack because it
 			 * checks both broken objects and links, but we only
@@ -913,14 +933,26 @@
 				     fsck_msg_types.buf);
 	}
 
+	if (index_pack_args) {
+		int i;
+
+		for (i = 0; i < cmd.args.nr; i++)
+			strvec_push(index_pack_args, cmd.args.v[i]);
+	}
+
 	cmd.in = demux.out;
 	cmd.git_cmd = 1;
 	if (start_command(&cmd))
 		die(_("fetch-pack: unable to fork off %s"), cmd_name);
-	if (do_keep && pack_lockfiles) {
-		char *pack_lockfile = index_pack_lockfile(cmd.out);
+	if (do_keep && (pack_lockfiles || fsck_objects)) {
+		int is_well_formed;
+		char *pack_lockfile = index_pack_lockfile(cmd.out, &is_well_formed);
+
+		if (!is_well_formed)
+			die(_("fetch-pack: invalid index-pack output"));
 		if (pack_lockfile)
 			string_list_append_nodup(pack_lockfiles, pack_lockfile);
+		parse_gitmodules_oids(cmd.out, gitmodules_oids);
 		close(cmd.out);
 	}
 
@@ -943,7 +975,7 @@
 	 * obtained .keep filename if necessary
 	 */
 	if (do_keep && pack_lockfiles && pack_lockfiles->nr && args->from_promisor)
-		write_promisor_file(pack_lockfiles->items[0].string, sought, nr_sought);
+		create_promisor_file(pack_lockfiles->items[0].string, sought, nr_sought);
 
 	return 0;
 }
@@ -955,6 +987,22 @@
 	return strcmp(a->name, b->name);
 }
 
+static void fsck_gitmodules_oids(struct oidset *gitmodules_oids)
+{
+	struct oidset_iter iter;
+	const struct object_id *oid;
+	struct fsck_options fo = FSCK_OPTIONS_STRICT;
+
+	if (!oidset_size(gitmodules_oids))
+		return;
+
+	oidset_iter_init(gitmodules_oids, &iter);
+	while ((oid = oidset_iter_next(&iter)))
+		register_found_gitmodules(oid);
+	if (fsck_finish(&fo))
+		die("fsck failed");
+}
+
 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 int fd[2],
 				 const struct ref *orig_ref,
@@ -969,6 +1017,7 @@
 	int agent_len;
 	struct fetch_negotiator negotiator_alloc;
 	struct fetch_negotiator *negotiator;
+	struct oidset gitmodules_oids = OIDSET_INIT;
 
 	negotiator = &negotiator_alloc;
 	fetch_negotiator_init(r, negotiator);
@@ -1084,8 +1133,10 @@
 		alternate_shallow_file = setup_temporary_shallow(si->shallow);
 	else
 		alternate_shallow_file = NULL;
-	if (get_pack(args, fd, pack_lockfiles, 1, sought, nr_sought))
+	if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
+		     &gitmodules_oids))
 		die(_("git fetch-pack: fetch failed."));
+	fsck_gitmodules_oids(&gitmodules_oids);
 
  all_done:
 	if (negotiator)
@@ -1535,6 +1586,8 @@
 	int seen_ack = 0;
 	struct string_list packfile_uris = STRING_LIST_INIT_DUP;
 	int i;
+	struct strvec index_pack_args = STRVEC_INIT;
+	struct oidset gitmodules_oids = OIDSET_INIT;
 
 	negotiator = &negotiator_alloc;
 	fetch_negotiator_init(r, negotiator);
@@ -1624,7 +1677,8 @@
 				receive_packfile_uris(&reader, &packfile_uris);
 			process_section_header(&reader, "packfile", 0);
 			if (get_pack(args, fd, pack_lockfiles,
-				     !packfile_uris.nr, sought, nr_sought))
+				     packfile_uris.nr ? &index_pack_args : NULL,
+				     sought, nr_sought, &gitmodules_oids))
 				die(_("git fetch-pack: fetch failed."));
 			do_check_stateless_delimiter(args, &reader);
 
@@ -1636,6 +1690,7 @@
 	}
 
 	for (i = 0; i < packfile_uris.nr; i++) {
+		int j;
 		struct child_process cmd = CHILD_PROCESS_INIT;
 		char packname[GIT_MAX_HEXSZ + 1];
 		const char *uri = packfile_uris.items[i].string +
@@ -1645,6 +1700,9 @@
 		strvec_pushf(&cmd.args, "--packfile=%.*s",
 			     (int) the_hash_algo->hexsz,
 			     packfile_uris.items[i].string);
+		for (j = 0; j < index_pack_args.nr; j++)
+			strvec_pushf(&cmd.args, "--index-pack-arg=%s",
+				     index_pack_args.v[j]);
 		strvec_push(&cmd.args, uri);
 		cmd.git_cmd = 1;
 		cmd.no_stdin = 1;
@@ -1663,6 +1721,8 @@
 
 		packname[the_hash_algo->hexsz] = '\0';
 
+		parse_gitmodules_oids(cmd.out, &gitmodules_oids);
+
 		close(cmd.out);
 
 		if (finish_command(&cmd))
@@ -1680,6 +1740,9 @@
 						 packname));
 	}
 	string_list_clear(&packfile_uris, 0);
+	strvec_clear(&index_pack_args);
+
+	fsck_gitmodules_oids(&gitmodules_oids);
 
 	if (negotiator)
 		negotiator->release(negotiator);
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 9a664a4..1e51492 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "object-store.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "revision.h"
 #include "tag.h"
 #include "string-list.h"
@@ -509,22 +510,28 @@
 	for (i = 0; i < origins.nr; i++) {
 		struct object_id *oid = origins.items[i].util;
 		enum object_type type;
-		unsigned long size, len;
+		unsigned long size;
 		char *buf = read_object_file(oid, &type, &size);
+		char *origbuf = buf;
+		unsigned long len = size;
 		struct signature_check sigc = { NULL };
-		struct strbuf sig = STRBUF_INIT;
+		struct strbuf payload = STRBUF_INIT, sig = STRBUF_INIT;
 
 		if (!buf || type != OBJ_TAG)
 			goto next;
-		len = parse_signature(buf, size);
 
-		if (size == len)
-			; /* merely annotated */
-		else if (check_signature(buf, len, buf + len, size - len, &sigc) &&
-			!sigc.gpg_output)
-			strbuf_addstr(&sig, "gpg verification failed.\n");
-		else
-			strbuf_addstr(&sig, sigc.gpg_output);
+		if (!parse_signature(buf, size, &payload, &sig))
+			;/* merely annotated */
+		else {
+			buf = payload.buf;
+			len = payload.len;
+			if (check_signature(payload.buf, payload.len, sig.buf,
+					 sig.len, &sigc) &&
+				!sigc.gpg_output)
+				strbuf_addstr(&sig, "gpg verification failed.\n");
+			else
+				strbuf_addstr(&sig, sigc.gpg_output);
+		}
 		signature_check_clear(&sigc);
 
 		if (!tag_number++) {
@@ -547,9 +554,10 @@
 					strlen(origins.items[i].string));
 			fmt_tag_signature(&tagbuf, &sig, buf, len);
 		}
+		strbuf_release(&payload);
 		strbuf_release(&sig);
 	next:
-		free(buf);
+		free(origbuf);
 	}
 	if (tagbuf.len) {
 		strbuf_addch(out, '\n');
@@ -670,7 +678,7 @@
 		head = lookup_commit_or_die(&head_oid, "HEAD");
 		repo_init_revisions(the_repository, &rev, NULL);
 		rev.commit_format = CMIT_FMT_ONELINE;
-		rev.ignore_merges = 1;
+		diff_merges_suppress(&rev);
 		rev.limited = 1;
 
 		strbuf_complete_line(out);
diff --git a/fsck.c b/fsck.c
index 5e282b3..e3030f3 100644
--- a/fsck.c
+++ b/fsck.c
@@ -80,7 +80,9 @@
 	/* infos (reported as warnings, but ignored by default) */ \
 	FUNC(GITMODULES_PARSE, INFO) \
 	FUNC(BAD_TAG_NAME, INFO) \
-	FUNC(MISSING_TAGGER_ENTRY, INFO)
+	FUNC(MISSING_TAGGER_ENTRY, INFO) \
+	/* ignored (elevated when requested) */ \
+	FUNC(EXTRA_HEADER_ENTRY, IGNORE)
 
 #define MSG_ID(id, msg_type) FSCK_MSG_##id,
 enum fsck_msg_id {
@@ -461,6 +463,11 @@
 				generation += power * (name[--len] - '0');
 			if (power > 1 && len && name[len - 1] == '~')
 				name_prefix_len = len - 1;
+			else {
+				/* Maybe a non-first parent, e.g. HEAD^2 */
+				generation = 0;
+				name_prefix_len = len;
+			}
 		}
 	}
 
@@ -911,6 +918,16 @@
 		    unsigned long size, struct fsck_options *options)
 {
 	struct object_id tagged_oid;
+	int tagged_type;
+	return fsck_tag_standalone(oid, buffer, size, options, &tagged_oid,
+				   &tagged_type);
+}
+
+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
+			unsigned long size, struct fsck_options *options,
+			struct object_id *tagged_oid,
+			int *tagged_type)
+{
 	int ret = 0;
 	char *eol;
 	struct strbuf sb = STRBUF_INIT;
@@ -924,7 +941,7 @@
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
 		goto done;
 	}
-	if (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\n') {
+	if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
 		if (ret)
 			goto done;
@@ -940,7 +957,8 @@
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
 		goto done;
 	}
-	if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
+	*tagged_type = type_from_string_gently(buffer, eol - buffer, 1);
+	if (*tagged_type < 0)
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
 	if (ret)
 		goto done;
@@ -974,6 +992,21 @@
 	}
 	else
 		ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
+	if (!*buffer)
+		goto done;
+
+	if (!starts_with(buffer, "\n")) {
+		/*
+		 * The verify_headers() check will allow
+		 * e.g. "[...]tagger <tagger>\nsome
+		 * garbage\n\nmessage" to pass, thinking "some
+		 * garbage" could be a custom header. E.g. "mktag"
+		 * doesn't want any unknown headers.
+		 */
+		ret = report(options, oid, OBJ_TAG, FSCK_MSG_EXTRA_HEADER_ENTRY, "invalid format - extra header(s) after 'tagger'");
+		if (ret)
+			goto done;
+	}
 
 done:
 	strbuf_release(&sb);
@@ -1243,6 +1276,11 @@
 	return 1;
 }
 
+void register_found_gitmodules(const struct object_id *oid)
+{
+	oidset_insert(&gitmodules_found, oid);
+}
+
 int fsck_finish(struct fsck_options *options)
 {
 	int ret = 0;
@@ -1284,3 +1322,27 @@
 	oidset_clear(&gitmodules_done);
 	return ret;
 }
+
+int fsck_config_internal(const char *var, const char *value, void *cb,
+			 struct fsck_options *options)
+{
+	if (strcmp(var, "fsck.skiplist") == 0) {
+		const char *path;
+		struct strbuf sb = STRBUF_INIT;
+
+		if (git_config_pathname(&path, var, value))
+			return 1;
+		strbuf_addf(&sb, "skiplist=%s", path);
+		free((char *)path);
+		fsck_set_msg_types(options, sb.buf);
+		strbuf_release(&sb);
+		return 0;
+	}
+
+	if (skip_prefix(var, "fsck.", &var)) {
+		fsck_set_msg_type(options, var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
diff --git a/fsck.h b/fsck.h
index 69cf715..733378f 100644
--- a/fsck.h
+++ b/fsck.h
@@ -62,6 +62,17 @@
 int fsck_object(struct object *obj, void *data, unsigned long size,
 	struct fsck_options *options);
 
+void register_found_gitmodules(const struct object_id *oid);
+
+/*
+ * fsck a tag, and pass info about it back to the caller. This is
+ * exposed fsck_object() internals for git-mktag(1).
+ */
+int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
+			unsigned long size, struct fsck_options *options,
+			struct object_id *tagged_oid,
+			int *tag_type);
+
 /*
  * Some fsck checks are context-dependent, and may end up queued; run this
  * after completing all fsck_object() calls in order to resolve any remaining
@@ -94,4 +105,11 @@
 const char *fsck_describe_object(struct fsck_options *options,
 				 const struct object_id *oid);
 
+/*
+ * git_config() callback for use by fsck-y tools that want to support
+ * fsck.<msg> fsck.skipList etc.
+ */
+int fsck_config_internal(const char *var, const char *value, void *cb,
+			 struct fsck_options *options);
+
 #endif
diff --git a/fsmonitor.c b/fsmonitor.c
index ca031c3..23f8a0c 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -13,14 +13,19 @@
 
 struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);
 
+static void assert_index_minimum(struct index_state *istate, size_t pos)
+{
+	if (pos > istate->cache_nr)
+		BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
+		    (uintmax_t)pos, istate->cache_nr);
+}
+
 static void fsmonitor_ewah_callback(size_t pos, void *is)
 {
 	struct index_state *istate = (struct index_state *)is;
 	struct cache_entry *ce;
 
-	if (pos >= istate->cache_nr)
-		BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" >= %u)",
-		    (uintmax_t)pos, istate->cache_nr);
+	assert_index_minimum(istate, pos + 1);
 
 	ce = istate->cache[pos];
 	ce->ce_flags &= ~CE_FSMONITOR_VALID;
@@ -82,12 +87,14 @@
 	}
 	istate->fsmonitor_dirty = fsmonitor_dirty;
 
-	if (!istate->split_index &&
-	    istate->fsmonitor_dirty->bit_size > istate->cache_nr)
-		BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
-		    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
+	if (!istate->split_index)
+		assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
 
-	trace_printf_key(&trace_fsmonitor, "read fsmonitor extension successful");
+	trace2_data_string("index", NULL, "extension/fsmn/read/token",
+			   istate->fsmonitor_last_update);
+	trace_printf_key(&trace_fsmonitor,
+			 "read fsmonitor extension successful '%s'",
+			 istate->fsmonitor_last_update);
 	return 0;
 }
 
@@ -110,10 +117,8 @@
 	uint32_t ewah_size = 0;
 	int fixup = 0;
 
-	if (!istate->split_index &&
-	    istate->fsmonitor_dirty->bit_size > istate->cache_nr)
-		BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
-		    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
+	if (!istate->split_index)
+		assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
 
 	put_be32(&hdr_version, INDEX_EXTENSION_VERSION2);
 	strbuf_add(sb, &hdr_version, sizeof(uint32_t));
@@ -133,7 +138,11 @@
 	put_be32(&ewah_size, sb->len - ewah_start);
 	memcpy(sb->buf + fixup, &ewah_size, sizeof(uint32_t));
 
-	trace_printf_key(&trace_fsmonitor, "write fsmonitor extension successful");
+	trace2_data_string("index", NULL, "extension/fsmn/write/token",
+			   istate->fsmonitor_last_update);
+	trace_printf_key(&trace_fsmonitor,
+			 "write fsmonitor extension successful '%s'",
+			 istate->fsmonitor_last_update);
 }
 
 /*
@@ -142,6 +151,7 @@
 static int query_fsmonitor(int version, const char *last_update, struct strbuf *query_result)
 {
 	struct child_process cp = CHILD_PROCESS_INIT;
+	int result;
 
 	if (!core_fsmonitor)
 		return -1;
@@ -152,16 +162,63 @@
 	cp.use_shell = 1;
 	cp.dir = get_git_work_tree();
 
-	return capture_command(&cp, query_result, 1024);
+	trace2_region_enter("fsm_hook", "query", NULL);
+
+	result = capture_command(&cp, query_result, 1024);
+
+	if (result)
+		trace2_data_intmax("fsm_hook", NULL, "query/failed", result);
+	else {
+		trace2_data_intmax("fsm_hook", NULL, "query/response-length",
+				   query_result->len);
+
+		if (fsmonitor_is_trivial_response(query_result))
+			trace2_data_intmax("fsm_hook", NULL,
+					   "query/trivial-response", 1);
+	}
+
+	trace2_region_leave("fsm_hook", "query", NULL);
+
+	return result;
 }
 
-static void fsmonitor_refresh_callback(struct index_state *istate, const char *name)
+int fsmonitor_is_trivial_response(const struct strbuf *query_result)
 {
-	int pos = index_name_pos(istate, name, strlen(name));
+	static char trivial_response[3] = { '\0', '/', '\0' };
+	int is_trivial = !memcmp(trivial_response,
+				 &query_result->buf[query_result->len - 3], 3);
 
-	if (pos >= 0) {
-		struct cache_entry *ce = istate->cache[pos];
-		ce->ce_flags &= ~CE_FSMONITOR_VALID;
+	return is_trivial;
+}
+
+static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
+{
+	int i, len = strlen(name);
+	if (name[len - 1] == '/') {
+
+		/*
+		 * TODO We should binary search to find the first path with
+		 * TODO this directory prefix.  Then linearly update entries
+		 * TODO while the prefix matches.  Taking care to search without
+		 * TODO the trailing slash -- because '/' sorts after a few
+		 * TODO interesting special chars, like '.' and ' '.
+		 */
+
+		/* Mark all entries for the folder invalid */
+		for (i = 0; i < istate->cache_nr; i++) {
+			if (istate->cache[i]->ce_flags & CE_FSMONITOR_VALID &&
+			    starts_with(istate->cache[i]->name, name))
+				istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
+		}
+		/* Need to remove the / from the path for the untracked cache */
+		name[len - 1] = '\0';
+	} else {
+		int pos = index_name_pos(istate, name, strlen(name));
+
+		if (pos >= 0) {
+			struct cache_entry *ce = istate->cache[pos];
+			ce->ce_flags &= ~CE_FSMONITOR_VALID;
+		}
 	}
 
 	/*
@@ -287,16 +344,45 @@
 	istate->fsmonitor_last_update = strbuf_detach(&last_update_token, NULL);
 }
 
+/*
+ * The caller wants to turn on FSMonitor.  And when the caller writes
+ * the index to disk, a FSMonitor extension should be included.  This
+ * requires that `istate->fsmonitor_last_update` not be NULL.  But we
+ * have not actually talked to a FSMonitor process yet, so we don't
+ * have an initial value for this field.
+ *
+ * For a protocol V1 FSMonitor process, this field is a formatted
+ * "nanoseconds since epoch" field.  However, for a protocol V2
+ * FSMonitor process, this field is an opaque token.
+ *
+ * Historically, `add_fsmonitor()` has initialized this field to the
+ * current time for protocol V1 processes.  There are lots of race
+ * conditions here, but that code has shipped...
+ *
+ * The only true solution is to use a V2 FSMonitor and get a current
+ * or default token value (that it understands), but we cannot do that
+ * until we have actually talked to an instance of the FSMonitor process
+ * (but the protocol requires that we send a token first...).
+ *
+ * For simplicity, just initialize like we have a V1 process and require
+ * that V2 processes adapt.
+ */
+static void initialize_fsmonitor_last_update(struct index_state *istate)
+{
+	struct strbuf last_update = STRBUF_INIT;
+
+	strbuf_addf(&last_update, "%"PRIu64"", getnanotime());
+	istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
+}
+
 void add_fsmonitor(struct index_state *istate)
 {
 	unsigned int i;
-	struct strbuf last_update = STRBUF_INIT;
 
 	if (!istate->fsmonitor_last_update) {
 		trace_printf_key(&trace_fsmonitor, "add fsmonitor");
 		istate->cache_changed |= FSMONITOR_CHANGED;
-		strbuf_addf(&last_update, "%"PRIu64"", getnanotime());
-		istate->fsmonitor_last_update = strbuf_detach(&last_update, NULL);
+		initialize_fsmonitor_last_update(istate);
 
 		/* reset the fsmonitor state */
 		for (i = 0; i < istate->cache_nr; i++)
@@ -335,9 +421,7 @@
 			}
 
 			/* Mark all previously saved entries as dirty */
-			if (istate->fsmonitor_dirty->bit_size > istate->cache_nr)
-				BUG("fsmonitor_dirty has more entries than the index (%"PRIuMAX" > %u)",
-				    (uintmax_t)istate->fsmonitor_dirty->bit_size, istate->cache_nr);
+			assert_index_minimum(istate, istate->fsmonitor_dirty->bit_size);
 			ewah_each_bit(istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
 
 			refresh_fsmonitor(istate);
diff --git a/fsmonitor.h b/fsmonitor.h
index 739318a..7f1794b 100644
--- a/fsmonitor.h
+++ b/fsmonitor.h
@@ -45,6 +45,11 @@
 void refresh_fsmonitor(struct index_state *istate);
 
 /*
+ * Does the received result contain the "trivial" response?
+ */
+int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+
+/*
  * Set the given cache entries CE_FSMONITOR_VALID bit. This should be
  * called any time the cache entry has been updated to reflect the
  * current state of the file on disk.
diff --git a/gettext.c b/gettext.c
index 1b56421..af2413b 100644
--- a/gettext.c
+++ b/gettext.c
@@ -65,14 +65,6 @@
 	return NULL;
 }
 
-int use_gettext_poison(void)
-{
-	static int poison_requested = -1;
-	if (poison_requested == -1)
-		poison_requested = git_env_bool("GIT_TEST_GETTEXT_POISON", 0);
-	return poison_requested;
-}
-
 #ifndef NO_GETTEXT
 static int test_vsnprintf(const char *fmt, ...)
 {
@@ -117,8 +109,6 @@
 	if (!podir)
 		podir = p = system_path(GIT_LOCALE_PATH);
 
-	use_gettext_poison(); /* getenv() reentrancy paranoia */
-
 	if (!is_directory(podir)) {
 		free(p);
 		return;
diff --git a/gettext.h b/gettext.h
index bee52eb..c8b34fd 100644
--- a/gettext.h
+++ b/gettext.h
@@ -28,15 +28,12 @@
 
 #define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
 
-int use_gettext_poison(void);
-
 #ifndef NO_GETTEXT
 void git_setup_gettext(void);
 int gettext_width(const char *s);
 #else
 static inline void git_setup_gettext(void)
 {
-	use_gettext_poison(); /* getenv() reentrancy paranoia */
 }
 static inline int gettext_width(const char *s)
 {
@@ -48,14 +45,12 @@
 {
 	if (!*msgid)
 		return "";
-	return use_gettext_poison() ? "# GETTEXT POISON #" : gettext(msgid);
+	return gettext(msgid);
 }
 
 static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
 const char *Q_(const char *msgid, const char *plu, unsigned long n)
 {
-	if (use_gettext_poison())
-		return "# GETTEXT POISON #";
 	return ngettext(msgid, plu, n);
 }
 
diff --git a/git-bisect.sh b/git-bisect.sh
index 1f3f6e9..6a7afae 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -39,21 +39,6 @@
 TERM_BAD=bad
 TERM_GOOD=good
 
-bisect_skip() {
-	all=''
-	for arg in "$@"
-	do
-		case "$arg" in
-		*..*)
-			revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
-		*)
-			revs=$(git rev-parse --sq-quote "$arg") ;;
-		esac
-		all="$all $revs"
-	done
-	eval git bisect--helper --bisect-state 'skip' $all
-}
-
 bisect_visualize() {
 	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
@@ -77,38 +62,6 @@
 	eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
-bisect_replay () {
-	file="$1"
-	test "$#" -eq 1 || die "$(gettext "No logfile given")"
-	test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
-	git bisect--helper --bisect-reset || exit
-	oIFS="$IFS" IFS="$IFS$(printf '\015')"
-	while read git bisect command rev tail
-	do
-		test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
-		if test "$git" = "git-bisect"
-		then
-			rev="$command"
-			command="$bisect"
-		fi
-		get_terms
-		git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
-		get_terms
-		case "$command" in
-		start)
-			eval "git bisect--helper --bisect-start $rev $tail" ;;
-		"$TERM_GOOD"|"$TERM_BAD"|skip)
-			git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
-		terms)
-			git bisect--helper --bisect-terms $rev || exit;;
-		*)
-			die "$(gettext "?? what are you talking about?")" ;;
-		esac
-	done <"$file"
-	IFS="$oIFS"
-	git bisect--helper --bisect-auto-next || exit
-}
-
 bisect_run () {
 	git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
@@ -169,11 +122,6 @@
 	done
 }
 
-bisect_log () {
-	test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
-	cat "$GIT_DIR/BISECT_LOG"
-}
-
 get_terms () {
 	if test -s "$GIT_DIR/BISECT_TERMS"
 	then
@@ -199,7 +147,7 @@
 	bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
 		git bisect--helper --bisect-state "$cmd" "$@" ;;
 	skip)
-		bisect_skip "$@" ;;
+		git bisect--helper --bisect-skip "$@" || exit;;
 	next)
 		# Not sure we want "next" at the UI level anymore.
 		git bisect--helper --bisect-next "$@" || exit ;;
@@ -208,9 +156,9 @@
 	reset)
 		git bisect--helper --bisect-reset "$@" ;;
 	replay)
-		bisect_replay "$@" ;;
+		git bisect--helper --bisect-replay "$@" || exit;;
 	log)
-		bisect_log ;;
+		git bisect--helper --bisect-log || exit ;;
 	run)
 		bisect_run "$@" ;;
 	terms)
diff --git a/git-compat-util.h b/git-compat-util.h
index 7d3db43..3b2738c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -252,9 +252,9 @@
 #ifdef PRECOMPOSE_UNICODE
 #include "compat/precompose_utf8.h"
 #else
-static inline void precompose_argv(int argc, const char **argv)
+static inline const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix)
 {
-	; /* nothing */
+	return prefix;
 }
 #define probe_utf8_pathname_composition()
 #endif
@@ -793,6 +793,12 @@
 		  const char *format, va_list ap);
 #endif
 
+#ifdef OPEN_RETURNS_EINTR
+#undef open
+#define open git_open_with_retry
+int git_open_with_retry(const char *path, int flag, ...);
+#endif
+
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
@@ -1181,9 +1187,12 @@
 #endif
 #endif
 
-#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__C99_MACRO_WITH_VA_ARGS)
+/*
+ * This is always defined as a first step towards making the use of variadic
+ * macros unconditional. If it causes compilation problems on your platform,
+ * please report it to the Git mailing list at git@vger.kernel.org.
+ */
 #define HAVE_VARIADIC_MACROS 1
-#endif
 
 /* usage.c: only to be used for testing BUG() implementation (see test-tool) */
 extern int BUG_exit_code;
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index 46af3e6..992124c 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -61,6 +61,9 @@
 		export BASE
 		eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
 	else
+		initialize_merge_tool "$merge_tool"
+		# ignore the error from the above --- run_merge_tool
+		# will diagnose unusable tool by itself
 		run_merge_tool "$merge_tool"
 	fi
 }
@@ -79,6 +82,9 @@
 then
 	LOCAL="$1"
 	REMOTE="$2"
+	initialize_merge_tool "$merge_tool"
+	# ignore the error from the above --- run_merge_tool
+	# will diagnose unusable tool by itself
 	run_merge_tool "$merge_tool" false
 else
 	# Launch the merge tool on each path provided by 'git diff'
diff --git a/git-gui/po/ru.po b/git-gui/po/ru.po
index 161ee1a..7aebaf8 100644
--- a/git-gui/po/ru.po
+++ b/git-gui/po/ru.po
@@ -331,7 +331,7 @@
 
 #: git-gui.sh:2936
 msgid "Unstage From Commit"
-msgstr "Убрать из издекса"
+msgstr "Убрать из индекса"
 
 #: git-gui.sh:2942 lib/index.tcl:521
 msgid "Revert Changes"
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 78f3647..542a6a7 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -166,6 +166,10 @@
 		return 1
 	}
 
+	hide_resolved_enabled () {
+		return 0
+	}
+
 	translate_merge_tool_path () {
 		echo "$1"
 	}
@@ -250,6 +254,10 @@
 	fi
 }
 
+initialize_merge_tool () {
+	# Bring tool-specific functions into scope
+	setup_tool "$1" || return 1
+}
 
 # Entry point for running tools
 run_merge_tool () {
@@ -261,9 +269,6 @@
 	merge_tool_path=$(get_merge_tool_path "$1") || exit
 	base_present="$2"
 
-	# Bring tool-specific functions into scope
-	setup_tool "$1" || return 1
-
 	if merge_mode
 	then
 		run_merge_cmd "$1"
diff --git a/git-mergetool.sh b/git-mergetool.sh
index e3f6d54..911470a 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -239,6 +239,13 @@
 	fi
 }
 
+hide_resolved () {
+	git merge-file --ours -q -p "$LOCAL" "$BASE" "$REMOTE" >"$LCONFL"
+	git merge-file --theirs -q -p "$LOCAL" "$BASE" "$REMOTE" >"$RCONFL"
+	mv -- "$LCONFL" "$LOCAL"
+	mv -- "$RCONFL" "$REMOTE"
+}
+
 merge_file () {
 	MERGED="$1"
 
@@ -265,6 +272,8 @@
 		ext=
 	esac
 
+	initialize_merge_tool "$merge_tool" || return
+
 	mergetool_tmpdir_init
 
 	if test "$MERGETOOL_TMPDIR" != "."
@@ -276,7 +285,9 @@
 
 	BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
 	LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+	LCONFL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_LCONFL_$$$ext"
 	REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+	RCONFL="$MERGETOOL_TMPDIR/${BASE}_REMOTE_RCONFL_$$$ext"
 	BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
 	base_mode= local_mode= remote_mode=
@@ -322,6 +333,45 @@
 	checkout_staged_file 2 "$MERGED" "$LOCAL"
 	checkout_staged_file 3 "$MERGED" "$REMOTE"
 
+	# hideResolved preferences hierarchy.
+	global_config="mergetool.hideResolved"
+	tool_config="mergetool.${merge_tool}.hideResolved"
+
+	if enabled=$(git config --type=bool "$tool_config")
+	then
+		# The user has a specific preference for a specific tool and no
+		# other preferences should override that.
+		: ;
+	elif enabled=$(git config --type=bool "$global_config")
+	then
+		# The user has a general preference for all tools.
+		#
+		# 'true' means the user likes the feature so we should use it
+		# where possible but tool authors can still override.
+		#
+		# 'false' means the user doesn't like the feature so we should
+		# not use it anywhere.
+		if test "$enabled" = true && hide_resolved_enabled
+		then
+		    enabled=true
+		else
+		    enabled=false
+		fi
+	else
+		# The user does not have a preference. Ask the tool.
+		if hide_resolved_enabled
+		then
+		    enabled=true
+		else
+		    enabled=false
+		fi
+	fi
+
+	if test "$enabled" = true
+	then
+		hide_resolved
+	fi
+
 	if test -z "$local_mode" || test -z "$remote_mode"
 	then
 		echo "Deleted merge conflict for '$MERGED':"
diff --git a/git-sh-i18n.sh b/git-sh-i18n.sh
index 8eef60b..e3d9f48 100644
--- a/git-sh-i18n.sh
+++ b/git-sh-i18n.sh
@@ -17,12 +17,7 @@
 
 # First decide what scheme to use...
 GIT_INTERNAL_GETTEXT_SH_SCHEME=fallthrough
-if test -n "$GIT_TEST_GETTEXT_POISON" &&
-	    git env--helper --type=bool --default=0 --exit-code \
-		GIT_TEST_GETTEXT_POISON
-then
-	GIT_INTERNAL_GETTEXT_SH_SCHEME=poison
-elif test -n "@@USE_GETTEXT_SCHEME@@"
+if test -n "@@USE_GETTEXT_SCHEME@@"
 then
 	GIT_INTERNAL_GETTEXT_SH_SCHEME="@@USE_GETTEXT_SCHEME@@"
 elif test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
@@ -63,21 +58,6 @@
 		)
 	}
 	;;
-poison)
-	# Emit garbage so that tests that incorrectly rely on translatable
-	# strings will fail.
-	gettext () {
-		printf "%s" "# GETTEXT POISON #"
-	}
-
-	eval_gettext () {
-		printf "%s" "# GETTEXT POISON #"
-	}
-
-	eval_ngettext () {
-		printf "%s" "# GETTEXT POISON #"
-	}
-	;;
 *)
 	gettext () {
 		printf "%s" "$1"
diff --git a/git.c b/git.c
index a00a0a4..9bc077a 100644
--- a/git.c
+++ b/git.c
@@ -29,6 +29,7 @@
 	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]\n"
 	   "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
+	   "           [--super-prefix=<path>] [--config-env=<name>=<envvar>]\n"
 	   "           <command> [<args>]");
 
 const char git_more_info_string[] =
@@ -254,6 +255,8 @@
 			git_config_push_parameter((*argv)[1]);
 			(*argv)++;
 			(*argc)--;
+		} else if (skip_prefix(cmd, "--config-env=", &cmd)) {
+			git_config_push_env(cmd);
 		} else if (!strcmp(cmd, "--literal-pathspecs")) {
 			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
 			if (envchanged)
@@ -420,7 +423,7 @@
 			int nongit_ok;
 			prefix = setup_git_directory_gently(&nongit_ok);
 		}
-
+		prefix = precompose_argv_prefix(argc, argv, prefix);
 		if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
 		    !(p->option & DELAY_PAGER_CONFIG))
 			use_pager = check_pager_config(p->cmd);
diff --git a/gpg-interface.c b/gpg-interface.c
index b499270..127aecf 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "commit.h"
 #include "config.h"
 #include "run-command.h"
 #include "strbuf.h"
@@ -345,7 +346,7 @@
 		fputs(output, stderr);
 }
 
-size_t parse_signature(const char *buf, size_t size)
+size_t parse_signed_buffer(const char *buf, size_t size)
 {
 	size_t len = 0;
 	size_t match = size;
@@ -361,6 +362,18 @@
 	return match;
 }
 
+int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature)
+{
+	size_t match = parse_signed_buffer(buf, size);
+	if (match != size) {
+		strbuf_add(payload, buf, match);
+		remove_signature(payload);
+		strbuf_add(signature, buf + match, size - match);
+		return 1;
+	}
+	return 0;
+}
+
 void set_signing_key(const char *key)
 {
 	free(configured_signing_key);
diff --git a/gpg-interface.h b/gpg-interface.h
index f4e9b4f..80567e4 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -38,12 +38,19 @@
 void signature_check_clear(struct signature_check *sigc);
 
 /*
+ * Look at a GPG signed tag object.  If such a signature exists, store it in
+ * signature and the signed content in payload.  Return 1 if a signature was
+ * found, and 0 otherwise.
+ */
+int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature);
+
+/*
  * Look at GPG signed content (e.g. a signed tag object), whose
  * payload is followed by a detached signature on it.  Return the
  * offset where the embedded detached signature begins, or the end of
  * the data when there is no such signature.
  */
-size_t parse_signature(const char *buf, size_t size);
+size_t parse_signed_buffer(const char *buf, size_t size);
 
 /*
  * Create a detached signature for the contents of "buffer" and append
diff --git a/grep.c b/grep.c
index efeb6dc..aabfaaa 100644
--- a/grep.c
+++ b/grep.c
@@ -166,11 +166,6 @@
 					pcre2_malloc, pcre2_free, NULL);
 #endif
 
-#ifdef USE_LIBPCRE1
-	pcre_malloc = malloc;
-	pcre_free = free;
-#endif
-
 	*opt = grep_defaults;
 
 	opt->repo = repo;
@@ -223,17 +218,7 @@
 		break;
 
 	case GREP_PATTERN_TYPE_PCRE:
-#ifdef USE_LIBPCRE2
 		opt->pcre2 = 1;
-#else
-		/*
-		 * It's important that pcre1 always be assigned to
-		 * even when there's no USE_LIBPCRE* defined. We still
-		 * call the PCRE stub function, it just dies with
-		 * "cannot use Perl-compatible regexes[...]".
-		 */
-		opt->pcre1 = 1;
-#endif
 		break;
 	}
 }
@@ -377,92 +362,6 @@
 	return 1;
 }
 
-#ifdef USE_LIBPCRE1
-static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
-{
-	const char *error;
-	int erroffset;
-	int options = PCRE_MULTILINE;
-	int study_options = 0;
-
-	if (opt->ignore_case) {
-		if (!opt->ignore_locale && has_non_ascii(p->pattern))
-			p->pcre1_tables = pcre_maketables();
-		options |= PCRE_CASELESS;
-	}
-	if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern))
-		options |= PCRE_UTF8;
-
-	p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
-				      p->pcre1_tables);
-	if (!p->pcre1_regexp)
-		compile_regexp_failed(p, error);
-
-#if defined(PCRE_CONFIG_JIT) && !defined(NO_LIBPCRE1_JIT)
-	pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on);
-	if (opt->debug)
-		fprintf(stderr, "pcre1_jit_on=%d\n", p->pcre1_jit_on);
-
-	if (p->pcre1_jit_on)
-		study_options = PCRE_STUDY_JIT_COMPILE;
-#endif
-
-	p->pcre1_extra_info = pcre_study(p->pcre1_regexp, study_options, &error);
-	if (!p->pcre1_extra_info && error)
-		die("%s", error);
-}
-
-static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
-		regmatch_t *match, int eflags)
-{
-	int ovector[30], ret, flags = PCRE_NO_UTF8_CHECK;
-
-	if (eflags & REG_NOTBOL)
-		flags |= PCRE_NOTBOL;
-
-	ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
-			eol - line, 0, flags, ovector,
-			ARRAY_SIZE(ovector));
-
-	if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
-		die("pcre_exec failed with error code %d", ret);
-	if (ret > 0) {
-		ret = 0;
-		match->rm_so = ovector[0];
-		match->rm_eo = ovector[1];
-	}
-
-	return ret;
-}
-
-static void free_pcre1_regexp(struct grep_pat *p)
-{
-	pcre_free(p->pcre1_regexp);
-#ifdef PCRE_CONFIG_JIT
-	if (p->pcre1_jit_on)
-		pcre_free_study(p->pcre1_extra_info);
-	else
-#endif
-		pcre_free(p->pcre1_extra_info);
-	pcre_free((void *)p->pcre1_tables);
-}
-#else /* !USE_LIBPCRE1 */
-static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
-{
-	die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
-}
-
-static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
-		regmatch_t *match, int eflags)
-{
-	return 1;
-}
-
-static void free_pcre1_regexp(struct grep_pat *p)
-{
-}
-#endif /* !USE_LIBPCRE1 */
-
 #ifdef USE_LIBPCRE2
 static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 {
@@ -492,7 +391,23 @@
 	}
 	if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
 	    !(!opt->ignore_case && (p->fixed || p->is_fixed)))
-		options |= PCRE2_UTF;
+		options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
+
+	/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
+	if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS)) {
+		struct strbuf buf;
+		int len;
+		int err;
+
+		if ((len = pcre2_config(PCRE2_CONFIG_VERSION, NULL)) < 0)
+			BUG("pcre2_config(..., NULL) failed: %d", len);
+		strbuf_init(&buf, len + 1);
+		if ((err = pcre2_config(PCRE2_CONFIG_VERSION, buf.buf)) < 0)
+			BUG("pcre2_config(..., buf.buf) failed: %d", err);
+		if (versioncmp(buf.buf, "10.36") < 0)
+			options |= PCRE2_NO_START_OPTIMIZE;
+		strbuf_release(&buf);
+	}
 
 	p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern,
 					 p->patternlen, options, &error, &erroffset,
@@ -508,8 +423,6 @@
 	}
 
 	pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
-	if (opt->debug)
-		fprintf(stderr, "pcre2_jit_on=%d\n", p->pcre2_jit_on);
 	if (p->pcre2_jit_on) {
 		jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
 		if (jitret)
@@ -535,9 +448,6 @@
 			BUG("pcre2_pattern_info() failed: %d", patinforet);
 		if (jitsizearg == 0) {
 			p->pcre2_jit_on = 0;
-			if (opt->debug)
-				fprintf(stderr, "pcre2_jit_on=%d: (*NO_JIT) in regex\n",
-					p->pcre2_jit_on);
 			return;
 		}
 	}
@@ -588,11 +498,6 @@
 #else /* !USE_LIBPCRE2 */
 static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 {
-	/*
-	 * Unreachable until USE_LIBPCRE2 becomes synonymous with
-	 * USE_LIBPCRE. See the sibling comment in
-	 * grep_set_pattern_type_option().
-	 */
 	die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
 }
 
@@ -616,8 +521,6 @@
 	if (opt->ignore_case)
 		regflags |= REG_ICASE;
 	err = regcomp(&p->regexp, sb.buf, regflags);
-	if (opt->debug)
-		fprintf(stderr, "fixed %s\n", sb.buf);
 	strbuf_release(&sb);
 	if (err) {
 		char errbuf[1024];
@@ -693,11 +596,6 @@
 		return;
 	}
 
-	if (opt->pcre1) {
-		compile_pcre1_regexp(p, opt);
-		return;
-	}
-
 	if (p->ignore_case)
 		regflags |= REG_ICASE;
 	if (opt->extended_regexp_option)
@@ -812,87 +710,6 @@
 	return compile_pattern_or(list);
 }
 
-static void indent(int in)
-{
-	while (in-- > 0)
-		fputc(' ', stderr);
-}
-
-static void dump_grep_pat(struct grep_pat *p)
-{
-	switch (p->token) {
-	case GREP_AND: fprintf(stderr, "*and*"); break;
-	case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
-	case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
-	case GREP_NOT: fprintf(stderr, "*not*"); break;
-	case GREP_OR: fprintf(stderr, "*or*"); break;
-
-	case GREP_PATTERN: fprintf(stderr, "pattern"); break;
-	case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
-	case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
-	}
-
-	switch (p->token) {
-	default: break;
-	case GREP_PATTERN_HEAD:
-		fprintf(stderr, "<head %d>", p->field); break;
-	case GREP_PATTERN_BODY:
-		fprintf(stderr, "<body>"); break;
-	}
-	switch (p->token) {
-	default: break;
-	case GREP_PATTERN_HEAD:
-	case GREP_PATTERN_BODY:
-	case GREP_PATTERN:
-		fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
-		break;
-	}
-	fputc('\n', stderr);
-}
-
-static void dump_grep_expression_1(struct grep_expr *x, int in)
-{
-	indent(in);
-	switch (x->node) {
-	case GREP_NODE_TRUE:
-		fprintf(stderr, "true\n");
-		break;
-	case GREP_NODE_ATOM:
-		dump_grep_pat(x->u.atom);
-		break;
-	case GREP_NODE_NOT:
-		fprintf(stderr, "(not\n");
-		dump_grep_expression_1(x->u.unary, in+1);
-		indent(in);
-		fprintf(stderr, ")\n");
-		break;
-	case GREP_NODE_AND:
-		fprintf(stderr, "(and\n");
-		dump_grep_expression_1(x->u.binary.left, in+1);
-		dump_grep_expression_1(x->u.binary.right, in+1);
-		indent(in);
-		fprintf(stderr, ")\n");
-		break;
-	case GREP_NODE_OR:
-		fprintf(stderr, "(or\n");
-		dump_grep_expression_1(x->u.binary.left, in+1);
-		dump_grep_expression_1(x->u.binary.right, in+1);
-		indent(in);
-		fprintf(stderr, ")\n");
-		break;
-	}
-}
-
-static void dump_grep_expression(struct grep_opt *opt)
-{
-	struct grep_expr *x = opt->pattern_expression;
-
-	if (opt->all_match)
-		fprintf(stderr, "[all-match]\n");
-	dump_grep_expression_1(x, 0);
-	fflush(NULL);
-}
-
 static struct grep_expr *grep_true_expr(void)
 {
 	struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -973,7 +790,7 @@
 	return z;
 }
 
-static void compile_grep_patterns_real(struct grep_opt *opt)
+void compile_grep_patterns(struct grep_opt *opt)
 {
 	struct grep_pat *p;
 	struct grep_expr *header_expr = prep_header_patterns(opt);
@@ -993,7 +810,7 @@
 
 	if (opt->all_match || header_expr)
 		opt->extended = 1;
-	else if (!opt->extended && !opt->debug)
+	else if (!opt->extended)
 		return;
 
 	p = opt->pattern_list;
@@ -1016,13 +833,6 @@
 	opt->all_match = 1;
 }
 
-void compile_grep_patterns(struct grep_opt *opt)
-{
-	compile_grep_patterns_real(opt);
-	if (opt->debug)
-		dump_grep_expression(opt);
-}
-
 static void free_pattern_expr(struct grep_expr *x)
 {
 	switch (x->node) {
@@ -1051,9 +861,7 @@
 		case GREP_PATTERN: /* atom */
 		case GREP_PATTERN_HEAD:
 		case GREP_PATTERN_BODY:
-			if (p->pcre1_regexp)
-				free_pcre1_regexp(p);
-			else if (p->pcre2_pattern)
+			if (p->pcre2_pattern)
 				free_pcre2_pattern(p);
 			else
 				regfree(&p->regexp);
@@ -1116,9 +924,7 @@
 {
 	int hit;
 
-	if (p->pcre1_regexp)
-		hit = !pcre1match(p, line, eol, match, eflags);
-	else if (p->pcre2_pattern)
+	if (p->pcre2_pattern)
 		hit = !pcre2match(p, line, eol, match, eflags);
 	else
 		hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
diff --git a/grep.h b/grep.h
index b5c4e22..ae89d62 100644
--- a/grep.h
+++ b/grep.h
@@ -1,15 +1,6 @@
 #ifndef GREP_H
 #define GREP_H
 #include "color.h"
-#ifdef USE_LIBPCRE1
-#include <pcre.h>
-#ifndef PCRE_NO_UTF8_CHECK
-#define PCRE_NO_UTF8_CHECK 0
-#endif
-#else
-typedef int pcre;
-typedef int pcre_extra;
-#endif
 #ifdef USE_LIBPCRE2
 #define PCRE2_CODE_UNIT_WIDTH 8
 #include <pcre2.h>
@@ -18,6 +9,10 @@
 typedef int pcre2_match_data;
 typedef int pcre2_compile_context;
 #endif
+#ifndef PCRE2_MATCH_INVALID_UTF
+/* PCRE2_MATCH_* dummy also with !USE_LIBPCRE2, for test-pcre2-config.c */
+#define PCRE2_MATCH_INVALID_UTF 0
+#endif
 #include "thread-utils.h"
 #include "userdiff.h"
 
@@ -71,10 +66,6 @@
 	size_t patternlen;
 	enum grep_header_field field;
 	regex_t regexp;
-	pcre *pcre1_regexp;
-	pcre_extra *pcre1_extra_info;
-	const unsigned char *pcre1_tables;
-	int pcre1_jit_on;
 	pcre2_code *pcre2_pattern;
 	pcre2_match_data *pcre2_match_data;
 	pcre2_compile_context *pcre2_compile_context;
@@ -136,7 +127,6 @@
 	int word_regexp;
 	int fixed;
 	int all_match;
-	int debug;
 #define GREP_BINARY_DEFAULT	0
 #define GREP_BINARY_NOMATCH	1
 #define GREP_BINARY_TEXT	2
@@ -144,7 +134,6 @@
 	int allow_textconv;
 	int extended;
 	int use_reflog_filter;
-	int pcre1;
 	int pcre2;
 	int relative;
 	int pathname;
diff --git a/sha1-lookup.c b/hash-lookup.c
similarity index 79%
rename from sha1-lookup.c
rename to hash-lookup.c
index 2918584..b98ed5e 100644
--- a/sha1-lookup.c
+++ b/hash-lookup.c
@@ -1,9 +1,9 @@
 #include "cache.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 
-static uint32_t take2(const unsigned char *sha1)
+static uint32_t take2(const struct object_id *oid, size_t ofs)
 {
-	return ((sha1[0] << 8) | sha1[1]);
+	return ((oid->hash[ofs] << 8) | oid->hash[ofs + 1]);
 }
 
 /*
@@ -47,11 +47,11 @@
  */
 /*
  * The table should contain "nr" elements.
- * The sha1 of element i (between 0 and nr - 1) should be returned
+ * The oid of element i (between 0 and nr - 1) should be returned
  * by "fn(i, table)".
  */
-int sha1_pos(const unsigned char *hash, void *table, size_t nr,
-	     sha1_access_fn fn)
+int oid_pos(const struct object_id *oid, const void *table, size_t nr,
+	    oid_access_fn fn)
 {
 	size_t hi = nr;
 	size_t lo = 0;
@@ -64,9 +64,9 @@
 		size_t lov, hiv, miv, ofs;
 
 		for (ofs = 0; ofs < the_hash_algo->rawsz - 2; ofs += 2) {
-			lov = take2(fn(0, table) + ofs);
-			hiv = take2(fn(nr - 1, table) + ofs);
-			miv = take2(hash + ofs);
+			lov = take2(fn(0, table), ofs);
+			hiv = take2(fn(nr - 1, table), ofs);
+			miv = take2(oid, ofs);
 			if (miv < lov)
 				return -1;
 			if (hiv < miv)
@@ -74,7 +74,7 @@
 			if (lov != hiv) {
 				/*
 				 * At this point miv could be equal
-				 * to hiv (but sha1 could still be higher);
+				 * to hiv (but hash could still be higher);
 				 * the invariant of (mi < hi) should be
 				 * kept.
 				 */
@@ -88,7 +88,7 @@
 
 	do {
 		int cmp;
-		cmp = hashcmp(fn(mi, table), hash);
+		cmp = oidcmp(fn(mi, table), oid);
 		if (!cmp)
 			return mi;
 		if (cmp > 0)
@@ -100,17 +100,17 @@
 	return index_pos_to_insert_pos(lo);
 }
 
-int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+int bsearch_hash(const unsigned char *hash, const uint32_t *fanout_nbo,
 		 const unsigned char *table, size_t stride, uint32_t *result)
 {
 	uint32_t hi, lo;
 
-	hi = ntohl(fanout_nbo[*sha1]);
-	lo = ((*sha1 == 0x0) ? 0 : ntohl(fanout_nbo[*sha1 - 1]));
+	hi = ntohl(fanout_nbo[*hash]);
+	lo = ((*hash == 0x0) ? 0 : ntohl(fanout_nbo[*hash - 1]));
 
 	while (lo < hi) {
 		unsigned mi = lo + (hi - lo) / 2;
-		int cmp = hashcmp(table + mi * stride, sha1);
+		int cmp = hashcmp(table + mi * stride, hash);
 
 		if (!cmp) {
 			if (result)
diff --git a/sha1-lookup.h b/hash-lookup.h
similarity index 64%
rename from sha1-lookup.h
rename to hash-lookup.h
index 5afcd01..dbd71eb 100644
--- a/sha1-lookup.h
+++ b/hash-lookup.h
@@ -1,20 +1,20 @@
-#ifndef SHA1_LOOKUP_H
-#define SHA1_LOOKUP_H
+#ifndef HASH_LOOKUP_H
+#define HASH_LOOKUP_H
 
-typedef const unsigned char *sha1_access_fn(size_t index, void *table);
+typedef const struct object_id *oid_access_fn(size_t index, const void *table);
 
-int sha1_pos(const unsigned char *sha1,
-	     void *table,
-	     size_t nr,
-	     sha1_access_fn fn);
+int oid_pos(const struct object_id *oid,
+	    const void *table,
+	    size_t nr,
+	    oid_access_fn fn);
 
 /*
- * Searches for sha1 in table, using the given fanout table to determine the
+ * Searches for hash in table, using the given fanout table to determine the
  * interval to search, then using binary search. Returns 1 if found, 0 if not.
  *
  * Takes the following parameters:
  *
- *  - sha1: the hash to search for
+ *  - hash: the hash to search for
  *  - fanout_nbo: a 256-element array of NETWORK-order 32-bit integers; the
  *    integer at position i represents the number of elements in table whose
  *    first byte is less than or equal to i
@@ -23,10 +23,10 @@
  *    GIT_MAX_RAWSZ or greater)
  *  - result: if not NULL, this function stores the element index of the
  *    position found (if the search is successful) or the index of the least
- *    element that is greater than sha1 (if the search is not successful)
+ *    element that is greater than hash (if the search is not successful)
  *
  * This function does not verify the validity of the fanout table.
  */
-int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
+int bsearch_hash(const unsigned char *hash, const uint32_t *fanout_nbo,
 		 const unsigned char *table, size_t stride, uint32_t *result);
 #endif
diff --git a/http-fetch.c b/http-fetch.c
index c4ccc5f..fa64246 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -3,6 +3,7 @@
 #include "exec-cmd.h"
 #include "http.h"
 #include "walker.h"
+#include "strvec.h"
 
 static const char http_fetch_usage[] = "git http-fetch "
 "[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
@@ -44,7 +45,8 @@
 }
 
 static void fetch_single_packfile(struct object_id *packfile_hash,
-				  const char *url) {
+				  const char *url,
+				  const char **index_pack_args) {
 	struct http_pack_request *preq;
 	struct slot_results results;
 	int ret;
@@ -55,7 +57,8 @@
 	if (preq == NULL)
 		die("couldn't create http pack request");
 	preq->slot->results = &results;
-	preq->generate_keep = 1;
+	preq->index_pack_args = index_pack_args;
+	preq->preserve_index_pack_stdout = 1;
 
 	if (start_active_slot(preq->slot)) {
 		run_active_slot(preq->slot);
@@ -86,6 +89,7 @@
 	int packfile = 0;
 	int nongit;
 	struct object_id packfile_hash;
+	struct strvec index_pack_args = STRVEC_INIT;
 
 	setup_git_directory_gently(&nongit);
 
@@ -112,6 +116,8 @@
 			packfile = 1;
 			if (parse_oid_hex(p, &packfile_hash, &end) || *end)
 				die(_("argument to --packfile must be a valid hash (got '%s')"), p);
+		} else if (skip_prefix(argv[arg], "--index-pack-arg=", &p)) {
+			strvec_push(&index_pack_args, p);
 		}
 		arg++;
 	}
@@ -124,10 +130,18 @@
 	git_config(git_default_config, NULL);
 
 	if (packfile) {
-		fetch_single_packfile(&packfile_hash, argv[arg]);
+		if (!index_pack_args.nr)
+			die(_("--packfile requires --index-pack-args"));
+
+		fetch_single_packfile(&packfile_hash, argv[arg],
+				      index_pack_args.v);
+
 		return 0;
 	}
 
+	if (index_pack_args.nr)
+		die(_("--index-pack-args can only be used with --packfile"));
+
 	if (commits_on_stdin) {
 		commits = walker_targets_stdin(&commit_id, &write_ref);
 	} else {
diff --git a/http.c b/http.c
index 8b23a54..f8ea28b 100644
--- a/http.c
+++ b/http.c
@@ -2259,6 +2259,9 @@
 	free(preq);
 }
 
+static const char *default_index_pack_args[] =
+	{"index-pack", "--stdin", NULL};
+
 int finish_http_pack_request(struct http_pack_request *preq)
 {
 	struct child_process ip = CHILD_PROCESS_INIT;
@@ -2270,17 +2273,15 @@
 
 	tmpfile_fd = xopen(preq->tmpfile.buf, O_RDONLY);
 
-	strvec_push(&ip.args, "index-pack");
-	strvec_push(&ip.args, "--stdin");
 	ip.git_cmd = 1;
 	ip.in = tmpfile_fd;
-	if (preq->generate_keep) {
-		strvec_pushf(&ip.args, "--keep=git %"PRIuMAX,
-			     (uintmax_t)getpid());
+	ip.argv = preq->index_pack_args ? preq->index_pack_args
+					: default_index_pack_args;
+
+	if (preq->preserve_index_pack_stdout)
 		ip.out = 0;
-	} else {
+	else
 		ip.no_stdout = 1;
-	}
 
 	if (run_command(&ip)) {
 		ret = -1;
diff --git a/http.h b/http.h
index 5de792e..bf3d127 100644
--- a/http.h
+++ b/http.h
@@ -218,12 +218,12 @@
 	char *url;
 
 	/*
-	 * If this is true, finish_http_pack_request() will pass "--keep" to
-	 * index-pack, resulting in the creation of a keep file, and will not
-	 * suppress its stdout (that is, the "keep\t<hash>\n" line will be
-	 * printed to stdout).
+	 * index-pack command to run. Must be terminated by NULL.
+	 *
+	 * If NULL, defaults to	{"index-pack", "--stdin", NULL}.
 	 */
-	unsigned generate_keep : 1;
+	const char **index_pack_args;
+	unsigned preserve_index_pack_stdout : 1;
 
 	FILE *packfile;
 	struct strbuf tmpfile;
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 0a3ef3c..4ec0041 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -21,7 +21,7 @@
  * in the traversal (until we mark it SEEN).  This is a way to
  * let us silently de-dup calls to show() in the caller.  This
  * is subtly different from the "revision.h:SHOWN" and the
- * "sha1-name.c:ONELINE_SEEN" bits.  And also different from
+ * "object-name.c:ONELINE_SEEN" bits.  And also different from
  * the non-de-dup usage in pack-bitmap.c
  */
 #define FILTER_SHOWN_BUT_REVISIT (1<<21)
diff --git a/log-tree.c b/log-tree.c
index fd0dde9..4531ceb 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -502,7 +502,7 @@
 	struct signature_check sigc = { 0 };
 	int status;
 
-	if (parse_signed_commit(commit, &payload, &signature) <= 0)
+	if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
 		goto out;
 
 	status = check_signature(payload.buf, payload.len, signature.buf,
@@ -548,7 +548,8 @@
 	struct strbuf verify_message;
 	struct signature_check sigc = { 0 };
 	int status, nth;
-	size_t payload_size;
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
 
 	hash_object_file(the_hash_algo, extra->value, extra->len,
 			 type_name(OBJ_TAG), &oid);
@@ -571,13 +572,11 @@
 		strbuf_addf(&verify_message,
 			    "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
 
-	payload_size = parse_signature(extra->value, extra->len);
 	status = -1;
-	if (extra->len > payload_size) {
+	if (parse_signature(extra->value, extra->len, &payload, &signature)) {
 		/* could have a good signature */
-		status = check_signature(extra->value, payload_size,
-					 extra->value + payload_size,
-					 extra->len - payload_size, &sigc);
+		status = check_signature(payload.buf, payload.len,
+					 signature.buf, signature.len, &sigc);
 		if (sigc.gpg_output)
 			strbuf_addstr(&verify_message, sigc.gpg_output);
 		else
@@ -588,6 +587,8 @@
 
 	show_sig_lines(opt, status, verify_message.buf);
 	strbuf_release(&verify_message);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
 	return 0;
 }
 
@@ -808,6 +809,11 @@
 	if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
 		struct diff_queue_struct dq;
 		struct diff_options opts;
+		struct range_diff_options range_diff_opts = {
+			.creation_factor = opt->creation_factor,
+			.dual_color = 1,
+			.diffopt = &opts
+		};
 
 		memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
 		DIFF_QUEUE_CLEAR(&diff_queued_diff);
@@ -822,8 +828,7 @@
 		opts.file = opt->diffopt.file;
 		opts.use_color = opt->diffopt.use_color;
 		diff_setup_done(&opts);
-		show_range_diff(opt->rdiff1, opt->rdiff2,
-				opt->creation_factor, 1, &opts, NULL);
+		show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
 
 		memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
 	}
@@ -899,15 +904,21 @@
 	int showed_log;
 	struct commit_list *parents;
 	struct object_id *oid;
+	int is_merge;
+	int all_need_diff = opt->diff || opt->diffopt.flags.exit_with_status;
 
-	if (!opt->diff && !opt->diffopt.flags.exit_with_status)
+	if (!all_need_diff && !opt->merges_need_diff)
 		return 0;
 
 	parse_commit_or_die(commit);
 	oid = get_commit_tree_oid(commit);
 
-	/* Root commit? */
 	parents = get_saved_parents(opt, commit);
+	is_merge = parents && parents->next;
+	if (!is_merge && !all_need_diff)
+		return 0;
+
+	/* Root commit? */
 	if (!parents) {
 		if (opt->show_root_diff) {
 			diff_root_tree_oid(oid, "", &opt->diffopt);
@@ -916,16 +927,16 @@
 		return !opt->loginfo;
 	}
 
-	/* More than one parent? */
-	if (parents->next) {
-		if (opt->ignore_merges)
-			return 0;
-		else if (opt->combine_merges)
+	if (is_merge) {
+		if (opt->combine_merges)
 			return do_diff_combined(opt, commit);
-		else if (!opt->first_parent_only) {
-			/* If we show multiple diffs, show the parent info */
-			log->parent = parents->item;
-		}
+		if (opt->separate_merges) {
+			if (!opt->first_parent_merges) {
+				/* Show parent info for multiple diffs */
+				log->parent = parents->item;
+			}
+		} else
+			return 0;
 	}
 
 	showed_log = 0;
@@ -941,7 +952,7 @@
 
 		/* Set up the log info for the next parent, if any.. */
 		parents = parents->next;
-		if (!parents || opt->first_parent_only)
+		if (!parents || opt->first_parent_merges)
 			break;
 		log->parent = parents->item;
 		opt->loginfo = log;
@@ -952,12 +963,14 @@
 int log_tree_commit(struct rev_info *opt, struct commit *commit)
 {
 	struct log_info log;
-	int shown, close_file = opt->diffopt.close_file;
+	int shown;
+	/* maybe called by e.g. cmd_log_walk(), maybe stand-alone */
+	int no_free = opt->diffopt.no_free;
 
 	log.commit = commit;
 	log.parent = NULL;
 	opt->loginfo = &log;
-	opt->diffopt.close_file = 0;
+	opt->diffopt.no_free = 1;
 
 	if (opt->line_level_traverse)
 		return line_log_print(opt, commit);
@@ -974,7 +987,7 @@
 		fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
 	opt->loginfo = NULL;
 	maybe_flush_or_die(opt->diffopt.file, "stdout");
-	if (close_file)
-		fclose(opt->diffopt.file);
+	opt->diffopt.no_free = no_free;
+	diff_free(&opt->diffopt);
 	return shown;
 }
diff --git a/ls-refs.c b/ls-refs.c
index a1e0b47..88f6c3f 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -7,6 +7,39 @@
 #include "pkt-line.h"
 #include "config.h"
 
+static int config_read;
+static int advertise_unborn;
+static int allow_unborn;
+
+static void ensure_config_read(void)
+{
+	const char *str = NULL;
+
+	if (config_read)
+		return;
+
+	if (repo_config_get_string_tmp(the_repository, "lsrefs.unborn", &str)) {
+		/*
+		 * If there is no such config, advertise and allow it by
+		 * default.
+		 */
+		advertise_unborn = 1;
+		allow_unborn = 1;
+	} else {
+		if (!strcmp(str, "advertise")) {
+			advertise_unborn = 1;
+			allow_unborn = 1;
+		} else if (!strcmp(str, "allow")) {
+			allow_unborn = 1;
+		} else if (!strcmp(str, "ignore")) {
+			/* do nothing */
+		} else {
+			die(_("invalid value '%s' for lsrefs.unborn"), str);
+		}
+	}
+	config_read = 1;
+}
+
 /*
  * Check if one of the prefixes is a prefix of the ref.
  * If no prefixes were provided, all refs match.
@@ -32,6 +65,7 @@
 	unsigned peel;
 	unsigned symrefs;
 	struct strvec prefixes;
+	unsigned unborn : 1;
 };
 
 static int send_ref(const char *refname, const struct object_id *oid,
@@ -47,7 +81,10 @@
 	if (!ref_match(&data->prefixes, refname_nons))
 		return 0;
 
-	strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
+	if (oid)
+		strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
+	else
+		strbuf_addf(&refline, "unborn %s", refname_nons);
 	if (data->symrefs && flag & REF_ISSYMREF) {
 		struct object_id unused;
 		const char *symref_target = resolve_ref_unsafe(refname, 0,
@@ -61,9 +98,9 @@
 			    strip_namespace(symref_target));
 	}
 
-	if (data->peel) {
+	if (data->peel && oid) {
 		struct object_id peeled;
-		if (!peel_ref(refname, &peeled))
+		if (!peel_iterated_oid(oid, &peeled))
 			strbuf_addf(&refline, " peeled:%s", oid_to_hex(&peeled));
 	}
 
@@ -74,6 +111,23 @@
 	return 0;
 }
 
+static void send_possibly_unborn_head(struct ls_refs_data *data)
+{
+	struct strbuf namespaced = STRBUF_INIT;
+	struct object_id oid;
+	int flag;
+	int oid_is_null;
+
+	strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
+	if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+		return; /* bad ref */
+	oid_is_null = is_null_oid(&oid);
+	if (!oid_is_null ||
+	    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
+		send_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);
+	strbuf_release(&namespaced);
+}
+
 static int ls_refs_config(const char *var, const char *value, void *data)
 {
 	/*
@@ -90,7 +144,9 @@
 	struct ls_refs_data data;
 
 	memset(&data, 0, sizeof(data));
+	strvec_init(&data.prefixes);
 
+	ensure_config_read();
 	git_config(ls_refs_config, NULL);
 
 	while (packet_reader_read(request) == PACKET_READ_NORMAL) {
@@ -103,14 +159,30 @@
 			data.symrefs = 1;
 		else if (skip_prefix(arg, "ref-prefix ", &out))
 			strvec_push(&data.prefixes, out);
+		else if (!strcmp("unborn", arg))
+			data.unborn = allow_unborn;
 	}
 
 	if (request->status != PACKET_READ_FLUSH)
 		die(_("expected flush after ls-refs arguments"));
 
-	head_ref_namespaced(send_ref, &data);
-	for_each_namespaced_ref(send_ref, &data);
+	send_possibly_unborn_head(&data);
+	if (!data.prefixes.nr)
+		strvec_push(&data.prefixes, "");
+	for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
+				     send_ref, &data, 0);
 	packet_flush(1);
 	strvec_clear(&data.prefixes);
 	return 0;
 }
+
+int ls_refs_advertise(struct repository *r, struct strbuf *value)
+{
+	if (value) {
+		ensure_config_read();
+		if (advertise_unborn)
+			strbuf_addstr(value, "unborn");
+	}
+
+	return 1;
+}
diff --git a/ls-refs.h b/ls-refs.h
index 7b33a7c..a99e4be 100644
--- a/ls-refs.h
+++ b/ls-refs.h
@@ -6,5 +6,6 @@
 struct packet_reader;
 int ls_refs(struct repository *r, struct strvec *keys,
 	    struct packet_reader *request);
+int ls_refs_advertise(struct repository *r, struct strbuf *value);
 
 #endif /* LS_REFS_H */
diff --git a/mailmap.c b/mailmap.c
index 962fd86..9bb9cf8 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -143,31 +143,13 @@
 	return (*right == '\0' ? NULL : right);
 }
 
-static void read_mailmap_line(struct string_list *map, char *buffer,
-			      char **repo_abbrev)
+static void read_mailmap_line(struct string_list *map, char *buffer)
 {
 	char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
-	if (buffer[0] == '#') {
-		static const char abbrev[] = "# repo-abbrev:";
-		int abblen = sizeof(abbrev) - 1;
-		int len = strlen(buffer);
 
-		if (!repo_abbrev)
-			return;
-
-		if (len && buffer[len - 1] == '\n')
-			buffer[--len] = 0;
-		if (!strncmp(buffer, abbrev, abblen)) {
-			char *cp;
-
-			free(*repo_abbrev);
-
-			for (cp = buffer + abblen; isspace(*cp); cp++)
-				; /* nothing */
-			*repo_abbrev = xstrdup(cp);
-		}
+	if (buffer[0] == '#')
 		return;
-	}
+
 	if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
 		parse_name_and_email(name2, &name2, &email2, 1);
 
@@ -175,8 +157,7 @@
 		add_mapping(map, name1, email1, name2, email2);
 }
 
-static int read_mailmap_file(struct string_list *map, const char *filename,
-			     char **repo_abbrev)
+static int read_mailmap_file(struct string_list *map, const char *filename)
 {
 	char buffer[1024];
 	FILE *f;
@@ -192,13 +173,12 @@
 	}
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL)
-		read_mailmap_line(map, buffer, repo_abbrev);
+		read_mailmap_line(map, buffer);
 	fclose(f);
 	return 0;
 }
 
-static void read_mailmap_string(struct string_list *map, char *buf,
-				char **repo_abbrev)
+static void read_mailmap_string(struct string_list *map, char *buf)
 {
 	while (*buf) {
 		char *end = strchrnul(buf, '\n');
@@ -206,14 +186,12 @@
 		if (*end)
 			*end++ = '\0';
 
-		read_mailmap_line(map, buf, repo_abbrev);
+		read_mailmap_line(map, buf);
 		buf = end;
 	}
 }
 
-static int read_mailmap_blob(struct string_list *map,
-			     const char *name,
-			     char **repo_abbrev)
+static int read_mailmap_blob(struct string_list *map, const char *name)
 {
 	struct object_id oid;
 	char *buf;
@@ -231,13 +209,13 @@
 	if (type != OBJ_BLOB)
 		return error("mailmap is not a blob: %s", name);
 
-	read_mailmap_string(map, buf, repo_abbrev);
+	read_mailmap_string(map, buf);
 
 	free(buf);
 	return 0;
 }
 
-int read_mailmap(struct string_list *map, char **repo_abbrev)
+int read_mailmap(struct string_list *map)
 {
 	int err = 0;
 
@@ -247,10 +225,11 @@
 	if (!git_mailmap_blob && is_bare_repository())
 		git_mailmap_blob = "HEAD:.mailmap";
 
-	err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
+	if (!startup_info->have_repository || !is_bare_repository())
+		err |= read_mailmap_file(map, ".mailmap");
 	if (startup_info->have_repository)
-		err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
-	err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
+		err |= read_mailmap_blob(map, git_mailmap_blob);
+	err |= read_mailmap_file(map, git_mailmap_file);
 	return err;
 }
 
diff --git a/mailmap.h b/mailmap.h
index d0e6564..7e99fcc 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -3,7 +3,7 @@
 
 struct string_list;
 
-int read_mailmap(struct string_list *map, char **repo_abbrev);
+int read_mailmap(struct string_list *map);
 void clear_mailmap(struct string_list *map);
 
 int map_user(struct string_list *map,
diff --git a/merge-ort.c b/merge-ort.c
index b487901..603d30c 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -17,20 +17,3501 @@
 #include "cache.h"
 #include "merge-ort.h"
 
+#include "alloc.h"
+#include "blob.h"
+#include "cache-tree.h"
+#include "commit.h"
+#include "commit-reach.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "dir.h"
+#include "ll-merge.h"
+#include "object-store.h"
+#include "revision.h"
+#include "strmap.h"
+#include "submodule.h"
+#include "tree.h"
+#include "unpack-trees.h"
+#include "xdiff-interface.h"
+
+/*
+ * We have many arrays of size 3.  Whenever we have such an array, the
+ * indices refer to one of the sides of the three-way merge.  This is so
+ * pervasive that the constants 0, 1, and 2 are used in many places in the
+ * code (especially in arithmetic operations to find the other side's index
+ * or to compute a relevant mask), but sometimes these enum names are used
+ * to aid code clarity.
+ *
+ * See also 'filemask' and 'dirmask' in struct conflict_info; the "ith side"
+ * referred to there is one of these three sides.
+ */
+enum merge_side {
+	MERGE_BASE = 0,
+	MERGE_SIDE1 = 1,
+	MERGE_SIDE2 = 2
+};
+
+struct rename_info {
+	/*
+	 * All variables that are arrays of size 3 correspond to data tracked
+	 * for the sides in enum merge_side.  Index 0 is almost always unused
+	 * because we often only need to track information for MERGE_SIDE1 and
+	 * MERGE_SIDE2 (MERGE_BASE can't have rename information since renames
+	 * are determined relative to what changed since the MERGE_BASE).
+	 */
+
+	/*
+	 * pairs: pairing of filenames from diffcore_rename()
+	 */
+	struct diff_queue_struct pairs[3];
+
+	/*
+	 * dirs_removed: directories removed on a given side of history.
+	 */
+	struct strset dirs_removed[3];
+
+	/*
+	 * dir_rename_count: tracking where parts of a directory were renamed to
+	 *
+	 * When files in a directory are renamed, they may not all go to the
+	 * same location.  Each strmap here tracks:
+	 *      old_dir => {new_dir => int}
+	 * That is, dir_rename_count[side] is a strmap to a strintmap.
+	 */
+	struct strmap dir_rename_count[3];
+
+	/*
+	 * dir_renames: computed directory renames
+	 *
+	 * This is a map of old_dir => new_dir and is derived in part from
+	 * dir_rename_count.
+	 */
+	struct strmap dir_renames[3];
+
+	/*
+	 * needed_limit: value needed for inexact rename detection to run
+	 *
+	 * If the current rename limit wasn't high enough for inexact
+	 * rename detection to run, this records the limit needed.  Otherwise,
+	 * this value remains 0.
+	 */
+	int needed_limit;
+};
+
+struct merge_options_internal {
+	/*
+	 * paths: primary data structure in all of merge ort.
+	 *
+	 * The keys of paths:
+	 *   * are full relative paths from the toplevel of the repository
+	 *     (e.g. "drivers/firmware/raspberrypi.c").
+	 *   * store all relevant paths in the repo, both directories and
+	 *     files (e.g. drivers, drivers/firmware would also be included)
+	 *   * these keys serve to intern all the path strings, which allows
+	 *     us to do pointer comparison on directory names instead of
+	 *     strcmp; we just have to be careful to use the interned strings.
+	 *     (Technically paths_to_free may track some strings that were
+	 *      removed from froms paths.)
+	 *
+	 * The values of paths:
+	 *   * either a pointer to a merged_info, or a conflict_info struct
+	 *   * merged_info contains all relevant information for a
+	 *     non-conflicted entry.
+	 *   * conflict_info contains a merged_info, plus any additional
+	 *     information about a conflict such as the higher orders stages
+	 *     involved and the names of the paths those came from (handy
+	 *     once renames get involved).
+	 *   * a path may start "conflicted" (i.e. point to a conflict_info)
+	 *     and then a later step (e.g. three-way content merge) determines
+	 *     it can be cleanly merged, at which point it'll be marked clean
+	 *     and the algorithm will ignore any data outside the contained
+	 *     merged_info for that entry
+	 *   * If an entry remains conflicted, the merged_info portion of a
+	 *     conflict_info will later be filled with whatever version of
+	 *     the file should be placed in the working directory (e.g. an
+	 *     as-merged-as-possible variation that contains conflict markers).
+	 */
+	struct strmap paths;
+
+	/*
+	 * conflicted: a subset of keys->values from "paths"
+	 *
+	 * conflicted is basically an optimization between process_entries()
+	 * and record_conflicted_index_entries(); the latter could loop over
+	 * ALL the entries in paths AGAIN and look for the ones that are
+	 * still conflicted, but since process_entries() has to loop over
+	 * all of them, it saves the ones it couldn't resolve in this strmap
+	 * so that record_conflicted_index_entries() can iterate just the
+	 * relevant entries.
+	 */
+	struct strmap conflicted;
+
+	/*
+	 * paths_to_free: additional list of strings to free
+	 *
+	 * If keys are removed from "paths", they are added to paths_to_free
+	 * to ensure they are later freed.  We avoid free'ing immediately since
+	 * other places (e.g. conflict_info.pathnames[]) may still be
+	 * referencing these paths.
+	 */
+	struct string_list paths_to_free;
+
+	/*
+	 * output: special messages and conflict notices for various paths
+	 *
+	 * This is a map of pathnames (a subset of the keys in "paths" above)
+	 * to strbufs.  It gathers various warning/conflict/notice messages
+	 * for later processing.
+	 */
+	struct strmap output;
+
+	/*
+	 * renames: various data relating to rename detection
+	 */
+	struct rename_info renames;
+
+	/*
+	 * current_dir_name, toplevel_dir: temporary vars
+	 *
+	 * These are used in collect_merge_info_callback(), and will set the
+	 * various merged_info.directory_name for the various paths we get;
+	 * see documentation for that variable and the requirements placed on
+	 * that field.
+	 */
+	const char *current_dir_name;
+	const char *toplevel_dir;
+
+	/* call_depth: recursion level counter for merging merge bases */
+	int call_depth;
+};
+
+struct version_info {
+	struct object_id oid;
+	unsigned short mode;
+};
+
+struct merged_info {
+	/* if is_null, ignore result.  otherwise result has oid & mode */
+	struct version_info result;
+	unsigned is_null:1;
+
+	/*
+	 * clean: whether the path in question is cleanly merged.
+	 *
+	 * see conflict_info.merged for more details.
+	 */
+	unsigned clean:1;
+
+	/*
+	 * basename_offset: offset of basename of path.
+	 *
+	 * perf optimization to avoid recomputing offset of final '/'
+	 * character in pathname (0 if no '/' in pathname).
+	 */
+	size_t basename_offset;
+
+	 /*
+	  * directory_name: containing directory name.
+	  *
+	  * Note that we assume directory_name is constructed such that
+	  *    strcmp(dir1_name, dir2_name) == 0 iff dir1_name == dir2_name,
+	  * i.e. string equality is equivalent to pointer equality.  For this
+	  * to hold, we have to be careful setting directory_name.
+	  */
+	const char *directory_name;
+};
+
+struct conflict_info {
+	/*
+	 * merged: the version of the path that will be written to working tree
+	 *
+	 * WARNING: It is critical to check merged.clean and ensure it is 0
+	 * before reading any conflict_info fields outside of merged.
+	 * Allocated merge_info structs will always have clean set to 1.
+	 * Allocated conflict_info structs will have merged.clean set to 0
+	 * initially.  The merged.clean field is how we know if it is safe
+	 * to access other parts of conflict_info besides merged; if a
+	 * conflict_info's merged.clean is changed to 1, the rest of the
+	 * algorithm is not allowed to look at anything outside of the
+	 * merged member anymore.
+	 */
+	struct merged_info merged;
+
+	/* oids & modes from each of the three trees for this path */
+	struct version_info stages[3];
+
+	/* pathnames for each stage; may differ due to rename detection */
+	const char *pathnames[3];
+
+	/* Whether this path is/was involved in a directory/file conflict */
+	unsigned df_conflict:1;
+
+	/*
+	 * Whether this path is/was involved in a non-content conflict other
+	 * than a directory/file conflict (e.g. rename/rename, rename/delete,
+	 * file location based on possible directory rename).
+	 */
+	unsigned path_conflict:1;
+
+	/*
+	 * For filemask and dirmask, the ith bit corresponds to whether the
+	 * ith entry is a file (filemask) or a directory (dirmask).  Thus,
+	 * filemask & dirmask is always zero, and filemask | dirmask is at
+	 * most 7 but can be less when a path does not appear as either a
+	 * file or a directory on at least one side of history.
+	 *
+	 * Note that these masks are related to enum merge_side, as the ith
+	 * entry corresponds to side i.
+	 *
+	 * These values come from a traverse_trees() call; more info may be
+	 * found looking at tree-walk.h's struct traverse_info,
+	 * particularly the documentation above the "fn" member (note that
+	 * filemask = mask & ~dirmask from that documentation).
+	 */
+	unsigned filemask:3;
+	unsigned dirmask:3;
+
+	/*
+	 * Optimization to track which stages match, to avoid the need to
+	 * recompute it in multiple steps. Either 0 or at least 2 bits are
+	 * set; if at least 2 bits are set, their corresponding stages match.
+	 */
+	unsigned match_mask:3;
+};
+
+/*** Function Grouping: various utility functions ***/
+
+/*
+ * For the next three macros, see warning for conflict_info.merged.
+ *
+ * In each of the below, mi is a struct merged_info*, and ci was defined
+ * as a struct conflict_info* (but we need to verify ci isn't actually
+ * pointed at a struct merged_info*).
+ *
+ * INITIALIZE_CI: Assign ci to mi but only if it's safe; set to NULL otherwise.
+ * VERIFY_CI: Ensure that something we assigned to a conflict_info* is one.
+ * ASSIGN_AND_VERIFY_CI: Similar to VERIFY_CI but do assignment first.
+ */
+#define INITIALIZE_CI(ci, mi) do {                                           \
+	(ci) = (!(mi) || (mi)->clean) ? NULL : (struct conflict_info *)(mi); \
+} while (0)
+#define VERIFY_CI(ci) assert(ci && !ci->merged.clean);
+#define ASSIGN_AND_VERIFY_CI(ci, mi) do {    \
+	(ci) = (struct conflict_info *)(mi);  \
+	assert((ci) && !(mi)->clean);        \
+} while (0)
+
+static void free_strmap_strings(struct strmap *map)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
+
+	strmap_for_each_entry(map, &iter, entry) {
+		free((char*)entry->key);
+	}
+}
+
+static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
+					  int reinitialize)
+{
+	struct rename_info *renames = &opti->renames;
+	int i;
+	void (*strmap_func)(struct strmap *, int) =
+		reinitialize ? strmap_partial_clear : strmap_clear;
+	void (*strset_func)(struct strset *) =
+		reinitialize ? strset_partial_clear : strset_clear;
+
+	/*
+	 * We marked opti->paths with strdup_strings = 0, so that we
+	 * wouldn't have to make another copy of the fullpath created by
+	 * make_traverse_path from setup_path_info().  But, now that we've
+	 * used it and have no other references to these strings, it is time
+	 * to deallocate them.
+	 */
+	free_strmap_strings(&opti->paths);
+	strmap_func(&opti->paths, 1);
+
+	/*
+	 * All keys and values in opti->conflicted are a subset of those in
+	 * opti->paths.  We don't want to deallocate anything twice, so we
+	 * don't free the keys and we pass 0 for free_values.
+	 */
+	strmap_func(&opti->conflicted, 0);
+
+	/*
+	 * opti->paths_to_free is similar to opti->paths; we created it with
+	 * strdup_strings = 0 to avoid making _another_ copy of the fullpath
+	 * but now that we've used it and have no other references to these
+	 * strings, it is time to deallocate them.  We do so by temporarily
+	 * setting strdup_strings to 1.
+	 */
+	opti->paths_to_free.strdup_strings = 1;
+	string_list_clear(&opti->paths_to_free, 0);
+	opti->paths_to_free.strdup_strings = 0;
+
+	/* Free memory used by various renames maps */
+	for (i = MERGE_SIDE1; i <= MERGE_SIDE2; ++i) {
+		struct hashmap_iter iter;
+		struct strmap_entry *entry;
+
+		strset_func(&renames->dirs_removed[i]);
+
+		strmap_for_each_entry(&renames->dir_rename_count[i],
+				      &iter, entry) {
+			struct strintmap *counts = entry->value;
+			strintmap_clear(counts);
+		}
+		strmap_func(&renames->dir_rename_count[i], 1);
+
+		strmap_func(&renames->dir_renames[i], 0);
+	}
+
+	if (!reinitialize) {
+		struct hashmap_iter iter;
+		struct strmap_entry *e;
+
+		/* Release and free each strbuf found in output */
+		strmap_for_each_entry(&opti->output, &iter, e) {
+			struct strbuf *sb = e->value;
+			strbuf_release(sb);
+			/*
+			 * While strictly speaking we don't need to free(sb)
+			 * here because we could pass free_values=1 when
+			 * calling strmap_clear() on opti->output, that would
+			 * require strmap_clear to do another
+			 * strmap_for_each_entry() loop, so we just free it
+			 * while we're iterating anyway.
+			 */
+			free(sb);
+		}
+		strmap_clear(&opti->output, 0);
+	}
+}
+
+static int err(struct merge_options *opt, const char *err, ...)
+{
+	va_list params;
+	struct strbuf sb = STRBUF_INIT;
+
+	strbuf_addstr(&sb, "error: ");
+	va_start(params, err);
+	strbuf_vaddf(&sb, err, params);
+	va_end(params);
+
+	error("%s", sb.buf);
+	strbuf_release(&sb);
+
+	return -1;
+}
+
+static void format_commit(struct strbuf *sb,
+			  int indent,
+			  struct commit *commit)
+{
+	struct merge_remote_desc *desc;
+	struct pretty_print_context ctx = {0};
+	ctx.abbrev = DEFAULT_ABBREV;
+
+	strbuf_addchars(sb, ' ', indent);
+	desc = merge_remote_util(commit);
+	if (desc) {
+		strbuf_addf(sb, "virtual %s\n", desc->name);
+		return;
+	}
+
+	format_commit_message(commit, "%h %s", sb, &ctx);
+	strbuf_addch(sb, '\n');
+}
+
+__attribute__((format (printf, 4, 5)))
+static void path_msg(struct merge_options *opt,
+		     const char *path,
+		     int omittable_hint, /* skippable under --remerge-diff */
+		     const char *fmt, ...)
+{
+	va_list ap;
+	struct strbuf *sb = strmap_get(&opt->priv->output, path);
+	if (!sb) {
+		sb = xmalloc(sizeof(*sb));
+		strbuf_init(sb, 0);
+		strmap_put(&opt->priv->output, path, sb);
+	}
+
+	va_start(ap, fmt);
+	strbuf_vaddf(sb, fmt, ap);
+	va_end(ap);
+
+	strbuf_addch(sb, '\n');
+}
+
+/* add a string to a strbuf, but converting "/" to "_" */
+static void add_flattened_path(struct strbuf *out, const char *s)
+{
+	size_t i = out->len;
+	strbuf_addstr(out, s);
+	for (; i < out->len; i++)
+		if (out->buf[i] == '/')
+			out->buf[i] = '_';
+}
+
+static char *unique_path(struct strmap *existing_paths,
+			 const char *path,
+			 const char *branch)
+{
+	struct strbuf newpath = STRBUF_INIT;
+	int suffix = 0;
+	size_t base_len;
+
+	strbuf_addf(&newpath, "%s~", path);
+	add_flattened_path(&newpath, branch);
+
+	base_len = newpath.len;
+	while (strmap_contains(existing_paths, newpath.buf)) {
+		strbuf_setlen(&newpath, base_len);
+		strbuf_addf(&newpath, "_%d", suffix++);
+	}
+
+	return strbuf_detach(&newpath, NULL);
+}
+
+/*** Function Grouping: functions related to collect_merge_info() ***/
+
+static void setup_path_info(struct merge_options *opt,
+			    struct string_list_item *result,
+			    const char *current_dir_name,
+			    int current_dir_name_len,
+			    char *fullpath, /* we'll take over ownership */
+			    struct name_entry *names,
+			    struct name_entry *merged_version,
+			    unsigned is_null,     /* boolean */
+			    unsigned df_conflict, /* boolean */
+			    unsigned filemask,
+			    unsigned dirmask,
+			    int resolved          /* boolean */)
+{
+	/* result->util is void*, so mi is a convenience typed variable */
+	struct merged_info *mi;
+
+	assert(!is_null || resolved);
+	assert(!df_conflict || !resolved); /* df_conflict implies !resolved */
+	assert(resolved == (merged_version != NULL));
+
+	mi = xcalloc(1, resolved ? sizeof(struct merged_info) :
+				   sizeof(struct conflict_info));
+	mi->directory_name = current_dir_name;
+	mi->basename_offset = current_dir_name_len;
+	mi->clean = !!resolved;
+	if (resolved) {
+		mi->result.mode = merged_version->mode;
+		oidcpy(&mi->result.oid, &merged_version->oid);
+		mi->is_null = !!is_null;
+	} else {
+		int i;
+		struct conflict_info *ci;
+
+		ASSIGN_AND_VERIFY_CI(ci, mi);
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+			ci->pathnames[i] = fullpath;
+			ci->stages[i].mode = names[i].mode;
+			oidcpy(&ci->stages[i].oid, &names[i].oid);
+		}
+		ci->filemask = filemask;
+		ci->dirmask = dirmask;
+		ci->df_conflict = !!df_conflict;
+		if (dirmask)
+			/*
+			 * Assume is_null for now, but if we have entries
+			 * under the directory then when it is complete in
+			 * write_completed_directory() it'll update this.
+			 * Also, for D/F conflicts, we have to handle the
+			 * directory first, then clear this bit and process
+			 * the file to see how it is handled -- that occurs
+			 * near the top of process_entry().
+			 */
+			mi->is_null = 1;
+	}
+	strmap_put(&opt->priv->paths, fullpath, mi);
+	result->string = fullpath;
+	result->util = mi;
+}
+
+static void add_pair(struct merge_options *opt,
+		     struct name_entry *names,
+		     const char *pathname,
+		     unsigned side,
+		     unsigned is_add /* if false, is_delete */)
+{
+	struct diff_filespec *one, *two;
+	struct rename_info *renames = &opt->priv->renames;
+	int names_idx = is_add ? side : 0;
+
+	one = alloc_filespec(pathname);
+	two = alloc_filespec(pathname);
+	fill_filespec(is_add ? two : one,
+		      &names[names_idx].oid, 1, names[names_idx].mode);
+	diff_queue(&renames->pairs[side], one, two);
+}
+
+static void collect_rename_info(struct merge_options *opt,
+				struct name_entry *names,
+				const char *dirname,
+				const char *fullname,
+				unsigned filemask,
+				unsigned dirmask,
+				unsigned match_mask)
+{
+	struct rename_info *renames = &opt->priv->renames;
+	unsigned side;
+
+	/* Update dirs_removed, as needed */
+	if (dirmask == 1 || dirmask == 3 || dirmask == 5) {
+		/* absent_mask = 0x07 - dirmask; sides = absent_mask/2 */
+		unsigned sides = (0x07 - dirmask)/2;
+		if (sides & 1)
+			strset_add(&renames->dirs_removed[1], fullname);
+		if (sides & 2)
+			strset_add(&renames->dirs_removed[2], fullname);
+	}
+
+	if (filemask == 0 || filemask == 7)
+		return;
+
+	for (side = MERGE_SIDE1; side <= MERGE_SIDE2; ++side) {
+		unsigned side_mask = (1 << side);
+
+		/* Check for deletion on side */
+		if ((filemask & 1) && !(filemask & side_mask))
+			add_pair(opt, names, fullname, side, 0 /* delete */);
+
+		/* Check for addition on side */
+		if (!(filemask & 1) && (filemask & side_mask))
+			add_pair(opt, names, fullname, side, 1 /* add */);
+	}
+}
+
+static int collect_merge_info_callback(int n,
+				       unsigned long mask,
+				       unsigned long dirmask,
+				       struct name_entry *names,
+				       struct traverse_info *info)
+{
+	/*
+	 * n is 3.  Always.
+	 * common ancestor (mbase) has mask 1, and stored in index 0 of names
+	 * head of side 1  (side1) has mask 2, and stored in index 1 of names
+	 * head of side 2  (side2) has mask 4, and stored in index 2 of names
+	 */
+	struct merge_options *opt = info->data;
+	struct merge_options_internal *opti = opt->priv;
+	struct string_list_item pi;  /* Path Info */
+	struct conflict_info *ci; /* typed alias to pi.util (which is void*) */
+	struct name_entry *p;
+	size_t len;
+	char *fullpath;
+	const char *dirname = opti->current_dir_name;
+	unsigned filemask = mask & ~dirmask;
+	unsigned match_mask = 0; /* will be updated below */
+	unsigned mbase_null = !(mask & 1);
+	unsigned side1_null = !(mask & 2);
+	unsigned side2_null = !(mask & 4);
+	unsigned side1_matches_mbase = (!side1_null && !mbase_null &&
+					names[0].mode == names[1].mode &&
+					oideq(&names[0].oid, &names[1].oid));
+	unsigned side2_matches_mbase = (!side2_null && !mbase_null &&
+					names[0].mode == names[2].mode &&
+					oideq(&names[0].oid, &names[2].oid));
+	unsigned sides_match = (!side1_null && !side2_null &&
+				names[1].mode == names[2].mode &&
+				oideq(&names[1].oid, &names[2].oid));
+
+	/*
+	 * Note: When a path is a file on one side of history and a directory
+	 * in another, we have a directory/file conflict.  In such cases, if
+	 * the conflict doesn't resolve from renames and deletions, then we
+	 * always leave directories where they are and move files out of the
+	 * way.  Thus, while struct conflict_info has a df_conflict field to
+	 * track such conflicts, we ignore that field for any directories at
+	 * a path and only pay attention to it for files at the given path.
+	 * The fact that we leave directories were they are also means that
+	 * we do not need to worry about getting additional df_conflict
+	 * information propagated from parent directories down to children
+	 * (unlike, say traverse_trees_recursive() in unpack-trees.c, which
+	 * sets a newinfo.df_conflicts field specifically to propagate it).
+	 */
+	unsigned df_conflict = (filemask != 0) && (dirmask != 0);
+
+	/* n = 3 is a fundamental assumption. */
+	if (n != 3)
+		BUG("Called collect_merge_info_callback wrong");
+
+	/*
+	 * A bunch of sanity checks verifying that traverse_trees() calls
+	 * us the way I expect.  Could just remove these at some point,
+	 * though maybe they are helpful to future code readers.
+	 */
+	assert(mbase_null == is_null_oid(&names[0].oid));
+	assert(side1_null == is_null_oid(&names[1].oid));
+	assert(side2_null == is_null_oid(&names[2].oid));
+	assert(!mbase_null || !side1_null || !side2_null);
+	assert(mask > 0 && mask < 8);
+
+	/* Determine match_mask */
+	if (side1_matches_mbase)
+		match_mask = (side2_matches_mbase ? 7 : 3);
+	else if (side2_matches_mbase)
+		match_mask = 5;
+	else if (sides_match)
+		match_mask = 6;
+
+	/*
+	 * Get the name of the relevant filepath, which we'll pass to
+	 * setup_path_info() for tracking.
+	 */
+	p = names;
+	while (!p->mode)
+		p++;
+	len = traverse_path_len(info, p->pathlen);
+
+	/* +1 in both of the following lines to include the NUL byte */
+	fullpath = xmalloc(len + 1);
+	make_traverse_path(fullpath, len + 1, info, p->path, p->pathlen);
+
+	/*
+	 * If mbase, side1, and side2 all match, we can resolve early.  Even
+	 * if these are trees, there will be no renames or anything
+	 * underneath.
+	 */
+	if (side1_matches_mbase && side2_matches_mbase) {
+		/* mbase, side1, & side2 all match; use mbase as resolution */
+		setup_path_info(opt, &pi, dirname, info->pathlen, fullpath,
+				names, names+0, mbase_null, 0,
+				filemask, dirmask, 1);
+		return mask;
+	}
+
+	/*
+	 * Gather additional information used in rename detection.
+	 */
+	collect_rename_info(opt, names, dirname, fullpath,
+			    filemask, dirmask, match_mask);
+
+	/*
+	 * Record information about the path so we can resolve later in
+	 * process_entries.
+	 */
+	setup_path_info(opt, &pi, dirname, info->pathlen, fullpath,
+			names, NULL, 0, df_conflict, filemask, dirmask, 0);
+
+	ci = pi.util;
+	VERIFY_CI(ci);
+	ci->match_mask = match_mask;
+
+	/* If dirmask, recurse into subdirectories */
+	if (dirmask) {
+		struct traverse_info newinfo;
+		struct tree_desc t[3];
+		void *buf[3] = {NULL, NULL, NULL};
+		const char *original_dir_name;
+		int i, ret;
+
+		ci->match_mask &= filemask;
+		newinfo = *info;
+		newinfo.prev = info;
+		newinfo.name = p->path;
+		newinfo.namelen = p->pathlen;
+		newinfo.pathlen = st_add3(newinfo.pathlen, p->pathlen, 1);
+		/*
+		 * If this directory we are about to recurse into cared about
+		 * its parent directory (the current directory) having a D/F
+		 * conflict, then we'd propagate the masks in this way:
+		 *    newinfo.df_conflicts |= (mask & ~dirmask);
+		 * But we don't worry about propagating D/F conflicts.  (See
+		 * comment near setting of local df_conflict variable near
+		 * the beginning of this function).
+		 */
+
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+			if (i == 1 && side1_matches_mbase)
+				t[1] = t[0];
+			else if (i == 2 && side2_matches_mbase)
+				t[2] = t[0];
+			else if (i == 2 && sides_match)
+				t[2] = t[1];
+			else {
+				const struct object_id *oid = NULL;
+				if (dirmask & 1)
+					oid = &names[i].oid;
+				buf[i] = fill_tree_descriptor(opt->repo,
+							      t + i, oid);
+			}
+			dirmask >>= 1;
+		}
+
+		original_dir_name = opti->current_dir_name;
+		opti->current_dir_name = pi.string;
+		ret = traverse_trees(NULL, 3, t, &newinfo);
+		opti->current_dir_name = original_dir_name;
+
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++)
+			free(buf[i]);
+
+		if (ret < 0)
+			return -1;
+	}
+
+	return mask;
+}
+
+static int collect_merge_info(struct merge_options *opt,
+			      struct tree *merge_base,
+			      struct tree *side1,
+			      struct tree *side2)
+{
+	int ret;
+	struct tree_desc t[3];
+	struct traverse_info info;
+
+	opt->priv->toplevel_dir = "";
+	opt->priv->current_dir_name = opt->priv->toplevel_dir;
+	setup_traverse_info(&info, opt->priv->toplevel_dir);
+	info.fn = collect_merge_info_callback;
+	info.data = opt;
+	info.show_all_errors = 1;
+
+	parse_tree(merge_base);
+	parse_tree(side1);
+	parse_tree(side2);
+	init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
+	init_tree_desc(t + 1, side1->buffer, side1->size);
+	init_tree_desc(t + 2, side2->buffer, side2->size);
+
+	trace2_region_enter("merge", "traverse_trees", opt->repo);
+	ret = traverse_trees(NULL, 3, t, &info);
+	trace2_region_leave("merge", "traverse_trees", opt->repo);
+
+	return ret;
+}
+
+/*** Function Grouping: functions related to threeway content merges ***/
+
+static int find_first_merges(struct repository *repo,
+			     const char *path,
+			     struct commit *a,
+			     struct commit *b,
+			     struct object_array *result)
+{
+	int i, j;
+	struct object_array merges = OBJECT_ARRAY_INIT;
+	struct commit *commit;
+	int contains_another;
+
+	char merged_revision[GIT_MAX_HEXSZ + 2];
+	const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+				   "--all", merged_revision, NULL };
+	struct rev_info revs;
+	struct setup_revision_opt rev_opts;
+
+	memset(result, 0, sizeof(struct object_array));
+	memset(&rev_opts, 0, sizeof(rev_opts));
+
+	/* get all revisions that merge commit a */
+	xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
+		  oid_to_hex(&a->object.oid));
+	repo_init_revisions(repo, &revs, NULL);
+	rev_opts.submodule = path;
+	/* FIXME: can't handle linked worktrees in submodules yet */
+	revs.single_worktree = path != NULL;
+	setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
+
+	/* save all revisions from the above list that contain b */
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+	while ((commit = get_revision(&revs)) != NULL) {
+		struct object *o = &(commit->object);
+		if (in_merge_bases(b, commit))
+			add_object_array(o, NULL, &merges);
+	}
+	reset_revision_walk();
+
+	/* Now we've got all merges that contain a and b. Prune all
+	 * merges that contain another found merge and save them in
+	 * result.
+	 */
+	for (i = 0; i < merges.nr; i++) {
+		struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+		contains_another = 0;
+		for (j = 0; j < merges.nr; j++) {
+			struct commit *m2 = (struct commit *) merges.objects[j].item;
+			if (i != j && in_merge_bases(m2, m1)) {
+				contains_another = 1;
+				break;
+			}
+		}
+
+		if (!contains_another)
+			add_object_array(merges.objects[i].item, NULL, result);
+	}
+
+	object_array_clear(&merges);
+	return result->nr;
+}
+
+static int merge_submodule(struct merge_options *opt,
+			   const char *path,
+			   const struct object_id *o,
+			   const struct object_id *a,
+			   const struct object_id *b,
+			   struct object_id *result)
+{
+	struct commit *commit_o, *commit_a, *commit_b;
+	int parent_count;
+	struct object_array merges;
+	struct strbuf sb = STRBUF_INIT;
+
+	int i;
+	int search = !opt->priv->call_depth;
+
+	/* store fallback answer in result in case we fail */
+	oidcpy(result, opt->priv->call_depth ? o : a);
+
+	/* we can not handle deletion conflicts */
+	if (is_null_oid(o))
+		return 0;
+	if (is_null_oid(a))
+		return 0;
+	if (is_null_oid(b))
+		return 0;
+
+	if (add_submodule_odb(path)) {
+		path_msg(opt, path, 0,
+			 _("Failed to merge submodule %s (not checked out)"),
+			 path);
+		return 0;
+	}
+
+	if (!(commit_o = lookup_commit_reference(opt->repo, o)) ||
+	    !(commit_a = lookup_commit_reference(opt->repo, a)) ||
+	    !(commit_b = lookup_commit_reference(opt->repo, b))) {
+		path_msg(opt, path, 0,
+			 _("Failed to merge submodule %s (commits not present)"),
+			 path);
+		return 0;
+	}
+
+	/* check whether both changes are forward */
+	if (!in_merge_bases(commit_o, commit_a) ||
+	    !in_merge_bases(commit_o, commit_b)) {
+		path_msg(opt, path, 0,
+			 _("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);
+		path_msg(opt, path, 1,
+			 _("Note: Fast-forwarding submodule %s to %s"),
+			 path, oid_to_hex(b));
+		return 1;
+	}
+	if (in_merge_bases(commit_b, commit_a)) {
+		oidcpy(result, a);
+		path_msg(opt, path, 1,
+			 _("Note: Fast-forwarding submodule %s to %s"),
+			 path, oid_to_hex(a));
+		return 1;
+	}
+
+	/*
+	 * Case #2: There are one or more merges that contain a and b in
+	 * the submodule. If there is only one, then present it as a
+	 * suggestion to the user, but leave it marked unmerged so the
+	 * user needs to confirm the resolution.
+	 */
+
+	/* Skip the search if makes no sense to the calling context.  */
+	if (!search)
+		return 0;
+
+	/* find commit which merges them */
+	parent_count = find_first_merges(opt->repo, path, commit_a, commit_b,
+					 &merges);
+	switch (parent_count) {
+	case 0:
+		path_msg(opt, path, 0, _("Failed to merge submodule %s"), path);
+		break;
+
+	case 1:
+		format_commit(&sb, 4,
+			      (struct commit *)merges.objects[0].item);
+		path_msg(opt, path, 0,
+			 _("Failed to merge submodule %s, but a possible merge "
+			   "resolution exists:\n%s\n"),
+			 path, sb.buf);
+		path_msg(opt, path, 1,
+			 _("If this is correct simply add it to the index "
+			   "for example\n"
+			   "by using:\n\n"
+			   "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+			   "which will accept this suggestion.\n"),
+			 oid_to_hex(&merges.objects[0].item->oid), path);
+		strbuf_release(&sb);
+		break;
+	default:
+		for (i = 0; i < merges.nr; i++)
+			format_commit(&sb, 4,
+				      (struct commit *)merges.objects[i].item);
+		path_msg(opt, path, 0,
+			 _("Failed to merge submodule %s, but multiple "
+			   "possible merges exist:\n%s"), path, sb.buf);
+		strbuf_release(&sb);
+	}
+
+	object_array_clear(&merges);
+	return 0;
+}
+
+static int merge_3way(struct merge_options *opt,
+		      const char *path,
+		      const struct object_id *o,
+		      const struct object_id *a,
+		      const struct object_id *b,
+		      const char *pathnames[3],
+		      const int extra_marker_size,
+		      mmbuffer_t *result_buf)
+{
+	mmfile_t orig, src1, src2;
+	struct ll_merge_options ll_opts = {0};
+	char *base, *name1, *name2;
+	int merge_status;
+
+	ll_opts.renormalize = opt->renormalize;
+	ll_opts.extra_marker_size = extra_marker_size;
+	ll_opts.xdl_opts = opt->xdl_opts;
+
+	if (opt->priv->call_depth) {
+		ll_opts.virtual_ancestor = 1;
+		ll_opts.variant = 0;
+	} else {
+		switch (opt->recursive_variant) {
+		case MERGE_VARIANT_OURS:
+			ll_opts.variant = XDL_MERGE_FAVOR_OURS;
+			break;
+		case MERGE_VARIANT_THEIRS:
+			ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
+			break;
+		default:
+			ll_opts.variant = 0;
+			break;
+		}
+	}
+
+	assert(pathnames[0] && pathnames[1] && pathnames[2] && opt->ancestor);
+	if (pathnames[0] == pathnames[1] && pathnames[1] == pathnames[2]) {
+		base  = mkpathdup("%s", opt->ancestor);
+		name1 = mkpathdup("%s", opt->branch1);
+		name2 = mkpathdup("%s", opt->branch2);
+	} else {
+		base  = mkpathdup("%s:%s", opt->ancestor, pathnames[0]);
+		name1 = mkpathdup("%s:%s", opt->branch1,  pathnames[1]);
+		name2 = mkpathdup("%s:%s", opt->branch2,  pathnames[2]);
+	}
+
+	read_mmblob(&orig, o);
+	read_mmblob(&src1, a);
+	read_mmblob(&src2, b);
+
+	merge_status = ll_merge(result_buf, path, &orig, base,
+				&src1, name1, &src2, name2,
+				opt->repo->index, &ll_opts);
+
+	free(base);
+	free(name1);
+	free(name2);
+	free(orig.ptr);
+	free(src1.ptr);
+	free(src2.ptr);
+	return merge_status;
+}
+
+static int handle_content_merge(struct merge_options *opt,
+				const char *path,
+				const struct version_info *o,
+				const struct version_info *a,
+				const struct version_info *b,
+				const char *pathnames[3],
+				const int extra_marker_size,
+				struct version_info *result)
+{
+	/*
+	 * path is the target location where we want to put the file, and
+	 * is used to determine any normalization rules in ll_merge.
+	 *
+	 * The normal case is that path and all entries in pathnames are
+	 * identical, though renames can affect which path we got one of
+	 * the three blobs to merge on various sides of history.
+	 *
+	 * extra_marker_size is the amount to extend conflict markers in
+	 * ll_merge; this is neeed if we have content merges of content
+	 * merges, which happens for example with rename/rename(2to1) and
+	 * rename/add conflicts.
+	 */
+	unsigned clean = 1;
+
+	/*
+	 * handle_content_merge() needs both files to be of the same type, i.e.
+	 * both files OR both submodules OR both symlinks.  Conflicting types
+	 * needs to be handled elsewhere.
+	 */
+	assert((S_IFMT & a->mode) == (S_IFMT & b->mode));
+
+	/* Merge modes */
+	if (a->mode == b->mode || a->mode == o->mode)
+		result->mode = b->mode;
+	else {
+		/* must be the 100644/100755 case */
+		assert(S_ISREG(a->mode));
+		result->mode = a->mode;
+		clean = (b->mode == o->mode);
+		/*
+		 * FIXME: If opt->priv->call_depth && !clean, then we really
+		 * should not make result->mode match either a->mode or
+		 * b->mode; that causes t6036 "check conflicting mode for
+		 * regular file" to fail.  It would be best to use some other
+		 * mode, but we'll confuse all kinds of stuff if we use one
+		 * where S_ISREG(result->mode) isn't true, and if we use
+		 * something like 0100666, then tree-walk.c's calls to
+		 * canon_mode() will just normalize that to 100644 for us and
+		 * thus not solve anything.
+		 *
+		 * Figure out if there's some kind of way we can work around
+		 * this...
+		 */
+	}
+
+	/*
+	 * Trivial oid merge.
+	 *
+	 * Note: While one might assume that the next four lines would
+	 * be unnecessary due to the fact that match_mask is often
+	 * setup and already handled, renames don't always take care
+	 * of that.
+	 */
+	if (oideq(&a->oid, &b->oid) || oideq(&a->oid, &o->oid))
+		oidcpy(&result->oid, &b->oid);
+	else if (oideq(&b->oid, &o->oid))
+		oidcpy(&result->oid, &a->oid);
+
+	/* Remaining rules depend on file vs. submodule vs. symlink. */
+	else if (S_ISREG(a->mode)) {
+		mmbuffer_t result_buf;
+		int ret = 0, merge_status;
+		int two_way;
+
+		/*
+		 * If 'o' is different type, treat it as null so we do a
+		 * two-way merge.
+		 */
+		two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+
+		merge_status = merge_3way(opt, path,
+					  two_way ? &null_oid : &o->oid,
+					  &a->oid, &b->oid,
+					  pathnames, extra_marker_size,
+					  &result_buf);
+
+		if ((merge_status < 0) || !result_buf.ptr)
+			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(opt, _("Unable to add %s to database"),
+				  path);
+
+		free(result_buf.ptr);
+		if (ret)
+			return -1;
+		clean &= (merge_status == 0);
+		path_msg(opt, path, 1, _("Auto-merging %s"), path);
+	} else if (S_ISGITLINK(a->mode)) {
+		int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+		clean = merge_submodule(opt, pathnames[0],
+					two_way ? &null_oid : &o->oid,
+					&a->oid, &b->oid, &result->oid);
+		if (opt->priv->call_depth && two_way && !clean) {
+			result->mode = o->mode;
+			oidcpy(&result->oid, &o->oid);
+		}
+	} else if (S_ISLNK(a->mode)) {
+		if (opt->priv->call_depth) {
+			clean = 0;
+			result->mode = o->mode;
+			oidcpy(&result->oid, &o->oid);
+		} else {
+			switch (opt->recursive_variant) {
+			case MERGE_VARIANT_NORMAL:
+				clean = 0;
+				oidcpy(&result->oid, &a->oid);
+				break;
+			case MERGE_VARIANT_OURS:
+				oidcpy(&result->oid, &a->oid);
+				break;
+			case MERGE_VARIANT_THEIRS:
+				oidcpy(&result->oid, &b->oid);
+				break;
+			}
+		}
+	} else
+		BUG("unsupported object type in the tree: %06o for %s",
+		    a->mode, path);
+
+	return clean;
+}
+
+/*** Function Grouping: functions related to detect_and_process_renames(), ***
+ *** which are split into directory and regular rename detection sections. ***/
+
+/*** Function Grouping: functions related to directory rename detection ***/
+
+struct collision_info {
+	struct string_list source_files;
+	unsigned reported_already:1;
+};
+
+/*
+ * Return a new string that replaces the beginning portion (which matches
+ * rename_info->key), with rename_info->util.new_dir.  In perl-speak:
+ *   new_path_name = (old_path =~ s/rename_info->key/rename_info->value/);
+ * NOTE:
+ *   Caller must ensure that old_path starts with rename_info->key + '/'.
+ */
+static char *apply_dir_rename(struct strmap_entry *rename_info,
+			      const char *old_path)
+{
+	struct strbuf new_path = STRBUF_INIT;
+	const char *old_dir = rename_info->key;
+	const char *new_dir = rename_info->value;
+	int oldlen, newlen, new_dir_len;
+
+	oldlen = strlen(old_dir);
+	if (*new_dir == '\0')
+		/*
+		 * If someone renamed/merged a subdirectory into the root
+		 * directory (e.g. 'some/subdir' -> ''), then we want to
+		 * avoid returning
+		 *     '' + '/filename'
+		 * as the rename; we need to make old_path + oldlen advance
+		 * past the '/' character.
+		 */
+		oldlen++;
+	new_dir_len = strlen(new_dir);
+	newlen = new_dir_len + (strlen(old_path) - oldlen) + 1;
+	strbuf_grow(&new_path, newlen);
+	strbuf_add(&new_path, new_dir, new_dir_len);
+	strbuf_addstr(&new_path, &old_path[oldlen]);
+
+	return strbuf_detach(&new_path, NULL);
+}
+
+static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask)
+{
+	struct merged_info *mi = strmap_get(paths, path);
+	struct conflict_info *ci;
+	if (!mi)
+		return 0;
+	INITIALIZE_CI(ci, mi);
+	return mi->clean || (side_mask & (ci->filemask | ci->dirmask));
+}
+
+/*
+ * See if there is a directory rename for path, and if there are any file
+ * level conflicts on the given side 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 *opt,
+					 const char *path,
+					 unsigned side_index,
+					 struct strmap_entry *rename_info,
+					 struct strmap *collisions)
+{
+	char *new_path = NULL;
+	struct collision_info *c_info;
+	int clean = 1;
+	struct strbuf collision_paths = STRBUF_INIT;
+
+	/*
+	 * entry has the mapping of old directory name to new directory name
+	 * that we want to apply to path.
+	 */
+	new_path = apply_dir_rename(rename_info, path);
+	if (!new_path)
+		BUG("Failed to apply directory rename!");
+
+	/*
+	 * The caller needs to have ensured that it has pre-populated
+	 * collisions with all paths that map to new_path.  Do a quick check
+	 * to ensure that's the case.
+	 */
+	c_info = strmap_get(collisions, new_path);
+	if (c_info == NULL)
+		BUG("c_info is NULL");
+
+	/*
+	 * Check for one-sided add/add/.../add conflicts, i.e.
+	 * where implicit renames from the other side doing
+	 * directory rename(s) can affect this side of history
+	 * to put multiple paths into the same location.  Warn
+	 * and bail on directory renames for such paths.
+	 */
+	if (c_info->reported_already) {
+		clean = 0;
+	} else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index)) {
+		c_info->reported_already = 1;
+		strbuf_add_separated_string_list(&collision_paths, ", ",
+						 &c_info->source_files);
+		path_msg(opt, new_path, 0,
+			 _("CONFLICT (implicit dir rename): Existing file/dir "
+			   "at %s in the way of implicit directory rename(s) "
+			   "putting the following path(s) there: %s."),
+		       new_path, collision_paths.buf);
+		clean = 0;
+	} else if (c_info->source_files.nr > 1) {
+		c_info->reported_already = 1;
+		strbuf_add_separated_string_list(&collision_paths, ", ",
+						 &c_info->source_files);
+		path_msg(opt, new_path, 0,
+			 _("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);
+		clean = 0;
+	}
+
+	/* Free memory we no longer need */
+	strbuf_release(&collision_paths);
+	if (!clean && new_path) {
+		free(new_path);
+		return NULL;
+	}
+
+	return new_path;
+}
+
+static void dirname_munge(char *filename)
+{
+	char *slash = strrchr(filename, '/');
+	if (!slash)
+		slash = filename;
+	*slash = '\0';
+}
+
+static void increment_count(struct strmap *dir_rename_count,
+			    char *old_dir,
+			    char *new_dir)
+{
+	struct strintmap *counts;
+	struct strmap_entry *e;
+
+	/* Get the {new_dirs -> counts} mapping using old_dir */
+	e = strmap_get_entry(dir_rename_count, old_dir);
+	if (e) {
+		counts = e->value;
+	} else {
+		counts = xmalloc(sizeof(*counts));
+		strintmap_init_with_options(counts, 0, NULL, 1);
+		strmap_put(dir_rename_count, old_dir, counts);
+	}
+
+	/* Increment the count for new_dir */
+	strintmap_incr(counts, new_dir, 1);
+}
+
+static void update_dir_rename_counts(struct strmap *dir_rename_count,
+				     struct strset *dirs_removed,
+				     const char *oldname,
+				     const char *newname)
+{
+	char *old_dir = xstrdup(oldname);
+	char *new_dir = xstrdup(newname);
+	char new_dir_first_char = new_dir[0];
+	int first_time_in_loop = 1;
+
+	while (1) {
+		dirname_munge(old_dir);
+		dirname_munge(new_dir);
+
+		/*
+		 * When renaming
+		 *   "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
+		 * then this suggests that both
+		 *   a/b/c/d/e/ => a/b/some/thing/else/e/
+		 *   a/b/c/d/   => a/b/some/thing/else/
+		 * so we want to increment counters for both.  We do NOT,
+		 * however, also want to suggest that there was the following
+		 * rename:
+		 *   a/b/c/ => a/b/some/thing/
+		 * so we need to quit at that point.
+		 *
+		 * Note the when first_time_in_loop, we only strip off the
+		 * basename, and we don't care if that's different.
+		 */
+		if (!first_time_in_loop) {
+			char *old_sub_dir = strchr(old_dir, '\0')+1;
+			char *new_sub_dir = strchr(new_dir, '\0')+1;
+			if (!*new_dir) {
+				/*
+				 * Special case when renaming to root directory,
+				 * i.e. when new_dir == "".  In this case, we had
+				 * something like
+				 *    a/b/subdir => subdir
+				 * and so dirname_munge() sets things up so that
+				 *    old_dir = "a/b\0subdir\0"
+				 *    new_dir = "\0ubdir\0"
+				 * We didn't have a '/' to overwrite a '\0' onto
+				 * in new_dir, so we have to compare differently.
+				 */
+				if (new_dir_first_char != old_sub_dir[0] ||
+				    strcmp(old_sub_dir+1, new_sub_dir))
+					break;
+			} else {
+				if (strcmp(old_sub_dir, new_sub_dir))
+					break;
+			}
+		}
+
+		if (strset_contains(dirs_removed, old_dir))
+			increment_count(dir_rename_count, old_dir, new_dir);
+		else
+			break;
+
+		/* If we hit toplevel directory ("") for old or new dir, quit */
+		if (!*old_dir || !*new_dir)
+			break;
+
+		first_time_in_loop = 0;
+	}
+
+	/* Free resources we don't need anymore */
+	free(old_dir);
+	free(new_dir);
+}
+
+static void compute_rename_counts(struct diff_queue_struct *pairs,
+				  struct strmap *dir_rename_count,
+				  struct strset *dirs_removed)
+{
+	int i;
+
+	for (i = 0; i < pairs->nr; ++i) {
+		struct diff_filepair *pair = pairs->queue[i];
+
+		/* File not part of directory rename if it wasn't renamed */
+		if (pair->status != 'R')
+			continue;
+
+		/*
+		 * Make dir_rename_count contain a map of a map:
+		 *   old_directory -> {new_directory -> count}
+		 * In other words, for every pair look at the directories for
+		 * the old filename and the new filename and count how many
+		 * times that pairing occurs.
+		 */
+		update_dir_rename_counts(dir_rename_count, dirs_removed,
+					 pair->one->path,
+					 pair->two->path);
+	}
+}
+
+static void get_provisional_directory_renames(struct merge_options *opt,
+					      unsigned side,
+					      int *clean)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
+	struct rename_info *renames = &opt->priv->renames;
+
+	compute_rename_counts(&renames->pairs[side],
+			      &renames->dir_rename_count[side],
+			      &renames->dirs_removed[side]);
+	/*
+	 * Collapse
+	 *    dir_rename_count: old_directory -> {new_directory -> count}
+	 * down to
+	 *    dir_renames: old_directory -> best_new_directory
+	 * where best_new_directory is the one with the unique highest count.
+	 */
+	strmap_for_each_entry(&renames->dir_rename_count[side], &iter, entry) {
+		const char *source_dir = entry->key;
+		struct strintmap *counts = entry->value;
+		struct hashmap_iter count_iter;
+		struct strmap_entry *count_entry;
+		int max = 0;
+		int bad_max = 0;
+		const char *best = NULL;
+
+		strintmap_for_each_entry(counts, &count_iter, count_entry) {
+			const char *target_dir = count_entry->key;
+			intptr_t count = (intptr_t)count_entry->value;
+
+			if (count == max)
+				bad_max = max;
+			else if (count > max) {
+				max = count;
+				best = target_dir;
+			}
+		}
+
+		if (bad_max == max) {
+			path_msg(opt, source_dir, 0,
+			       _("CONFLICT (directory rename split): "
+				 "Unclear where to rename %s to; it was "
+				 "renamed to multiple other directories, with "
+				 "no destination getting a majority of the "
+				 "files."),
+			       source_dir);
+			/*
+			 * We should mark this as unclean IF something attempts
+			 * to use this rename.  We do not yet have the logic
+			 * in place to detect if this directory rename is being
+			 * used, and optimizations that reduce the number of
+			 * renames cause this to falsely trigger.  For now,
+			 * just disable it, causing t6423 testcase 2a to break.
+			 * We'll later fix the detection, and when we do we
+			 * will re-enable setting *clean to 0 (and thereby fix
+			 * t6423 testcase 2a).
+			 */
+			/*   *clean = 0;   */
+		} else {
+			strmap_put(&renames->dir_renames[side],
+				   source_dir, (void*)best);
+		}
+	}
+}
+
+static void handle_directory_level_conflicts(struct merge_options *opt)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
+	struct string_list duplicated = STRING_LIST_INIT_NODUP;
+	struct rename_info *renames = &opt->priv->renames;
+	struct strmap *side1_dir_renames = &renames->dir_renames[MERGE_SIDE1];
+	struct strmap *side2_dir_renames = &renames->dir_renames[MERGE_SIDE2];
+	int i;
+
+	strmap_for_each_entry(side1_dir_renames, &iter, entry) {
+		if (strmap_contains(side2_dir_renames, entry->key))
+			string_list_append(&duplicated, entry->key);
+	}
+
+	for (i = 0; i < duplicated.nr; i++) {
+		strmap_remove(side1_dir_renames, duplicated.items[i].string, 0);
+		strmap_remove(side2_dir_renames, duplicated.items[i].string, 0);
+	}
+	string_list_clear(&duplicated, 0);
+}
+
+static struct strmap_entry *check_dir_renamed(const char *path,
+					      struct strmap *dir_renames)
+{
+	char *temp = xstrdup(path);
+	char *end;
+	struct strmap_entry *e = NULL;
+
+	while ((end = strrchr(temp, '/'))) {
+		*end = '\0';
+		e = strmap_get_entry(dir_renames, temp);
+		if (e)
+			break;
+	}
+	free(temp);
+	return e;
+}
+
+static void compute_collisions(struct strmap *collisions,
+			       struct strmap *dir_renames,
+			       struct diff_queue_struct *pairs)
+{
+	int i;
+
+	strmap_init_with_options(collisions, NULL, 0);
+	if (strmap_empty(dir_renames))
+		return;
+
+	/*
+	 * Multiple files can be mapped to the same path due to directory
+	 * renames done by the other side of history.  Since that other
+	 * side of history could have merged multiple directories into one,
+	 * if our side of history added the same file basename to each of
+	 * those directories, then all N of them would get implicitly
+	 * renamed by the directory rename detection into the same path,
+	 * and we'd get an add/add/.../add conflict, and all those adds
+	 * from *this* side of history.  This is not representable in the
+	 * index, and users aren't going to easily be able to make sense of
+	 * it.  So we need to provide a good warning about what's
+	 * happening, and fall back to no-directory-rename detection
+	 * behavior for those paths.
+	 *
+	 * See testcases 9e and all of section 5 from t6043 for examples.
+	 */
+	for (i = 0; i < pairs->nr; ++i) {
+		struct strmap_entry *rename_info;
+		struct collision_info *collision_info;
+		char *new_path;
+		struct diff_filepair *pair = pairs->queue[i];
+
+		if (pair->status != 'A' && pair->status != 'R')
+			continue;
+		rename_info = check_dir_renamed(pair->two->path, dir_renames);
+		if (!rename_info)
+			continue;
+
+		new_path = apply_dir_rename(rename_info, pair->two->path);
+		assert(new_path);
+		collision_info = strmap_get(collisions, new_path);
+		if (collision_info) {
+			free(new_path);
+		} else {
+			collision_info = xcalloc(1,
+						 sizeof(struct collision_info));
+			string_list_init(&collision_info->source_files, 0);
+			strmap_put(collisions, new_path, collision_info);
+		}
+		string_list_insert(&collision_info->source_files,
+				   pair->two->path);
+	}
+}
+
+static char *check_for_directory_rename(struct merge_options *opt,
+					const char *path,
+					unsigned side_index,
+					struct strmap *dir_renames,
+					struct strmap *dir_rename_exclusions,
+					struct strmap *collisions,
+					int *clean_merge)
+{
+	char *new_path = NULL;
+	struct strmap_entry *rename_info;
+	struct strmap_entry *otherinfo = NULL;
+	const char *new_dir;
+
+	if (strmap_empty(dir_renames))
+		return new_path;
+	rename_info = check_dir_renamed(path, dir_renames);
+	if (!rename_info)
+		return new_path;
+	/* old_dir = rename_info->key; */
+	new_dir = rename_info->value;
+
+	/*
+	 * This next part is a little weird.  We do not want to do an
+	 * implicit rename into a directory we renamed on our side, because
+	 * that will result in a spurious rename/rename(1to2) conflict.  An
+	 * example:
+	 *   Base commit: dumbdir/afile, otherdir/bfile
+	 *   Side 1:      smrtdir/afile, otherdir/bfile
+	 *   Side 2:      dumbdir/afile, dumbdir/bfile
+	 * Here, while working on Side 1, we could notice that otherdir was
+	 * renamed/merged to dumbdir, and change the diff_filepair for
+	 * otherdir/bfile into a rename into dumbdir/bfile.  However, Side
+	 * 2 will notice the rename from dumbdir to smrtdir, and do the
+	 * transitive rename to move it from dumbdir/bfile to
+	 * smrtdir/bfile.  That gives us bfile in dumbdir vs being in
+	 * smrtdir, a rename/rename(1to2) conflict.  We really just want
+	 * the file to end up in smrtdir.  And the way to achieve that is
+	 * to not let Side1 do the rename to dumbdir, since we know that is
+	 * the source of one of our directory renames.
+	 *
+	 * That's why otherinfo and dir_rename_exclusions is here.
+	 *
+	 * As it turns out, this also prevents N-way transient rename
+	 * confusion; See testcases 9c and 9d of t6043.
+	 */
+	otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir);
+	if (otherinfo) {
+		path_msg(opt, rename_info->key, 1,
+			 _("WARNING: Avoiding applying %s -> %s rename "
+			   "to %s, because %s itself was renamed."),
+			 rename_info->key, new_dir, path, new_dir);
+		return NULL;
+	}
+
+	new_path = handle_path_level_conflicts(opt, path, side_index,
+					       rename_info, collisions);
+	*clean_merge &= (new_path != NULL);
+
+	return new_path;
+}
+
+static void apply_directory_rename_modifications(struct merge_options *opt,
+						 struct diff_filepair *pair,
+						 char *new_path)
+{
+	/*
+	 * The basic idea is to get the conflict_info from opt->priv->paths
+	 * at old path, and insert it into new_path; basically just this:
+	 *     ci = strmap_get(&opt->priv->paths, old_path);
+	 *     strmap_remove(&opt->priv->paths, old_path, 0);
+	 *     strmap_put(&opt->priv->paths, new_path, ci);
+	 * However, there are some factors complicating this:
+	 *     - opt->priv->paths may already have an entry at new_path
+	 *     - Each ci tracks its containing directory, so we need to
+	 *       update that
+	 *     - If another ci has the same containing directory, then
+	 *       the two char*'s MUST point to the same location.  See the
+	 *       comment in struct merged_info.  strcmp equality is not
+	 *       enough; we need pointer equality.
+	 *     - opt->priv->paths must hold the parent directories of any
+	 *       entries that are added.  So, if this directory rename
+	 *       causes entirely new directories, we must recursively add
+	 *       parent directories.
+	 *     - For each parent directory added to opt->priv->paths, we
+	 *       also need to get its parent directory stored in its
+	 *       conflict_info->merged.directory_name with all the same
+	 *       requirements about pointer equality.
+	 */
+	struct string_list dirs_to_insert = STRING_LIST_INIT_NODUP;
+	struct conflict_info *ci, *new_ci;
+	struct strmap_entry *entry;
+	const char *branch_with_new_path, *branch_with_dir_rename;
+	const char *old_path = pair->two->path;
+	const char *parent_name;
+	const char *cur_path;
+	int i, len;
+
+	entry = strmap_get_entry(&opt->priv->paths, old_path);
+	old_path = entry->key;
+	ci = entry->value;
+	VERIFY_CI(ci);
+
+	/* Find parent directories missing from opt->priv->paths */
+	cur_path = new_path;
+	while (1) {
+		/* Find the parent directory of cur_path */
+		char *last_slash = strrchr(cur_path, '/');
+		if (last_slash) {
+			parent_name = xstrndup(cur_path, last_slash - cur_path);
+		} else {
+			parent_name = opt->priv->toplevel_dir;
+			break;
+		}
+
+		/* Look it up in opt->priv->paths */
+		entry = strmap_get_entry(&opt->priv->paths, parent_name);
+		if (entry) {
+			free((char*)parent_name);
+			parent_name = entry->key; /* reuse known pointer */
+			break;
+		}
+
+		/* Record this is one of the directories we need to insert */
+		string_list_append(&dirs_to_insert, parent_name);
+		cur_path = parent_name;
+	}
+
+	/* Traverse dirs_to_insert and insert them into opt->priv->paths */
+	for (i = dirs_to_insert.nr-1; i >= 0; --i) {
+		struct conflict_info *dir_ci;
+		char *cur_dir = dirs_to_insert.items[i].string;
+
+		dir_ci = xcalloc(1, sizeof(*dir_ci));
+
+		dir_ci->merged.directory_name = parent_name;
+		len = strlen(parent_name);
+		/* len+1 because of trailing '/' character */
+		dir_ci->merged.basename_offset = (len > 0 ? len+1 : len);
+		dir_ci->dirmask = ci->filemask;
+		strmap_put(&opt->priv->paths, cur_dir, dir_ci);
+
+		parent_name = cur_dir;
+	}
+
+	/*
+	 * We are removing old_path from opt->priv->paths.  old_path also will
+	 * eventually need to be freed, but it may still be used by e.g.
+	 * ci->pathnames.  So, store it in another string-list for now.
+	 */
+	string_list_append(&opt->priv->paths_to_free, old_path);
+
+	assert(ci->filemask == 2 || ci->filemask == 4);
+	assert(ci->dirmask == 0);
+	strmap_remove(&opt->priv->paths, old_path, 0);
+
+	branch_with_new_path   = (ci->filemask == 2) ? opt->branch1 : opt->branch2;
+	branch_with_dir_rename = (ci->filemask == 2) ? opt->branch2 : opt->branch1;
+
+	/* Now, finally update ci and stick it into opt->priv->paths */
+	ci->merged.directory_name = parent_name;
+	len = strlen(parent_name);
+	ci->merged.basename_offset = (len > 0 ? len+1 : len);
+	new_ci = strmap_get(&opt->priv->paths, new_path);
+	if (!new_ci) {
+		/* Place ci back into opt->priv->paths, but at new_path */
+		strmap_put(&opt->priv->paths, new_path, ci);
+	} else {
+		int index;
+
+		/* A few sanity checks */
+		VERIFY_CI(new_ci);
+		assert(ci->filemask == 2 || ci->filemask == 4);
+		assert((new_ci->filemask & ci->filemask) == 0);
+		assert(!new_ci->merged.clean);
+
+		/* Copy stuff from ci into new_ci */
+		new_ci->filemask |= ci->filemask;
+		if (new_ci->dirmask)
+			new_ci->df_conflict = 1;
+		index = (ci->filemask >> 1);
+		new_ci->pathnames[index] = ci->pathnames[index];
+		new_ci->stages[index].mode = ci->stages[index].mode;
+		oidcpy(&new_ci->stages[index].oid, &ci->stages[index].oid);
+
+		free(ci);
+		ci = new_ci;
+	}
+
+	if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE) {
+		/* Notify user of updated path */
+		if (pair->status == 'A')
+			path_msg(opt, new_path, 1,
+				 _("Path updated: %s added in %s inside a "
+				   "directory that was renamed in %s; moving "
+				   "it to %s."),
+				 old_path, branch_with_new_path,
+				 branch_with_dir_rename, new_path);
+		else
+			path_msg(opt, new_path, 1,
+				 _("Path updated: %s renamed to %s in %s, "
+				   "inside a directory that was renamed in %s; "
+				   "moving it to %s."),
+				 pair->one->path, old_path, branch_with_new_path,
+				 branch_with_dir_rename, new_path);
+	} else {
+		/*
+		 * opt->detect_directory_renames has the value
+		 * MERGE_DIRECTORY_RENAMES_CONFLICT, so mark these as conflicts.
+		 */
+		ci->path_conflict = 1;
+		if (pair->status == 'A')
+			path_msg(opt, new_path, 0,
+				 _("CONFLICT (file location): %s added in %s "
+				   "inside a directory that was renamed in %s, "
+				   "suggesting it should perhaps be moved to "
+				   "%s."),
+				 old_path, branch_with_new_path,
+				 branch_with_dir_rename, new_path);
+		else
+			path_msg(opt, new_path, 0,
+				 _("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."),
+				 pair->one->path, old_path, branch_with_new_path,
+				 branch_with_dir_rename, new_path);
+	}
+
+	/*
+	 * Finally, record the new location.
+	 */
+	pair->two->path = new_path;
+}
+
+/*** Function Grouping: functions related to regular rename detection ***/
+
+static int process_renames(struct merge_options *opt,
+			   struct diff_queue_struct *renames)
+{
+	int clean_merge = 1, i;
+
+	for (i = 0; i < renames->nr; ++i) {
+		const char *oldpath = NULL, *newpath;
+		struct diff_filepair *pair = renames->queue[i];
+		struct conflict_info *oldinfo = NULL, *newinfo = NULL;
+		struct strmap_entry *old_ent, *new_ent;
+		unsigned int old_sidemask;
+		int target_index, other_source_index;
+		int source_deleted, collision, type_changed;
+		const char *rename_branch = NULL, *delete_branch = NULL;
+
+		old_ent = strmap_get_entry(&opt->priv->paths, pair->one->path);
+		new_ent = strmap_get_entry(&opt->priv->paths, pair->two->path);
+		if (old_ent) {
+			oldpath = old_ent->key;
+			oldinfo = old_ent->value;
+		}
+		newpath = pair->two->path;
+		if (new_ent) {
+			newpath = new_ent->key;
+			newinfo = new_ent->value;
+		}
+
+		/*
+		 * If pair->one->path isn't in opt->priv->paths, that means
+		 * that either directory rename detection removed that
+		 * path, or a parent directory of oldpath was resolved and
+		 * we don't even need the rename; in either case, we can
+		 * skip it.  If oldinfo->merged.clean, then the other side
+		 * of history had no changes to oldpath and we don't need
+		 * the rename and can skip it.
+		 */
+		if (!oldinfo || oldinfo->merged.clean)
+			continue;
+
+		/*
+		 * diff_filepairs have copies of pathnames, thus we have to
+		 * use standard 'strcmp()' (negated) instead of '=='.
+		 */
+		if (i + 1 < renames->nr &&
+		    !strcmp(oldpath, renames->queue[i+1]->one->path)) {
+			/* Handle rename/rename(1to2) or rename/rename(1to1) */
+			const char *pathnames[3];
+			struct version_info merged;
+			struct conflict_info *base, *side1, *side2;
+			unsigned was_binary_blob = 0;
+
+			pathnames[0] = oldpath;
+			pathnames[1] = newpath;
+			pathnames[2] = renames->queue[i+1]->two->path;
+
+			base = strmap_get(&opt->priv->paths, pathnames[0]);
+			side1 = strmap_get(&opt->priv->paths, pathnames[1]);
+			side2 = strmap_get(&opt->priv->paths, pathnames[2]);
+
+			VERIFY_CI(base);
+			VERIFY_CI(side1);
+			VERIFY_CI(side2);
+
+			if (!strcmp(pathnames[1], pathnames[2])) {
+				/* Both sides renamed the same way */
+				assert(side1 == side2);
+				memcpy(&side1->stages[0], &base->stages[0],
+				       sizeof(merged));
+				side1->filemask |= (1 << MERGE_BASE);
+				/* Mark base as resolved by removal */
+				base->merged.is_null = 1;
+				base->merged.clean = 1;
+
+				/* We handled both renames, i.e. i+1 handled */
+				i++;
+				/* Move to next rename */
+				continue;
+			}
+
+			/* This is a rename/rename(1to2) */
+			clean_merge = handle_content_merge(opt,
+							   pair->one->path,
+							   &base->stages[0],
+							   &side1->stages[1],
+							   &side2->stages[2],
+							   pathnames,
+							   1 + 2 * opt->priv->call_depth,
+							   &merged);
+			if (!clean_merge &&
+			    merged.mode == side1->stages[1].mode &&
+			    oideq(&merged.oid, &side1->stages[1].oid))
+				was_binary_blob = 1;
+			memcpy(&side1->stages[1], &merged, sizeof(merged));
+			if (was_binary_blob) {
+				/*
+				 * Getting here means we were attempting to
+				 * merge a binary blob.
+				 *
+				 * Since we can't merge binaries,
+				 * handle_content_merge() just takes one
+				 * side.  But we don't want to copy the
+				 * contents of one side to both paths.  We
+				 * used the contents of side1 above for
+				 * side1->stages, let's use the contents of
+				 * side2 for side2->stages below.
+				 */
+				oidcpy(&merged.oid, &side2->stages[2].oid);
+				merged.mode = side2->stages[2].mode;
+			}
+			memcpy(&side2->stages[2], &merged, sizeof(merged));
+
+			side1->path_conflict = 1;
+			side2->path_conflict = 1;
+			/*
+			 * TODO: For renames we normally remove the path at the
+			 * old name.  It would thus seem consistent to do the
+			 * same for rename/rename(1to2) cases, but we haven't
+			 * done so traditionally and a number of the regression
+			 * tests now encode an expectation that the file is
+			 * left there at stage 1.  If we ever decide to change
+			 * this, add the following two lines here:
+			 *    base->merged.is_null = 1;
+			 *    base->merged.clean = 1;
+			 * and remove the setting of base->path_conflict to 1.
+			 */
+			base->path_conflict = 1;
+			path_msg(opt, oldpath, 0,
+				 _("CONFLICT (rename/rename): %s renamed to "
+				   "%s in %s and to %s in %s."),
+				 pathnames[0],
+				 pathnames[1], opt->branch1,
+				 pathnames[2], opt->branch2);
+
+			i++; /* We handled both renames, i.e. i+1 handled */
+			continue;
+		}
+
+		VERIFY_CI(oldinfo);
+		VERIFY_CI(newinfo);
+		target_index = pair->score; /* from collect_renames() */
+		assert(target_index == 1 || target_index == 2);
+		other_source_index = 3 - target_index;
+		old_sidemask = (1 << other_source_index); /* 2 or 4 */
+		source_deleted = (oldinfo->filemask == 1);
+		collision = ((newinfo->filemask & old_sidemask) != 0);
+		type_changed = !source_deleted &&
+			(S_ISREG(oldinfo->stages[other_source_index].mode) !=
+			 S_ISREG(newinfo->stages[target_index].mode));
+		if (type_changed && collision) {
+			/*
+			 * special handling so later blocks can handle this...
+			 *
+			 * if type_changed && collision are both true, then this
+			 * was really a double rename, but one side wasn't
+			 * detected due to lack of break detection.  I.e.
+			 * something like
+			 *    orig: has normal file 'foo'
+			 *    side1: renames 'foo' to 'bar', adds 'foo' symlink
+			 *    side2: renames 'foo' to 'bar'
+			 * In this case, the foo->bar rename on side1 won't be
+			 * detected because the new symlink named 'foo' is
+			 * there and we don't do break detection.  But we detect
+			 * this here because we don't want to merge the content
+			 * of the foo symlink with the foo->bar file, so we
+			 * have some logic to handle this special case.  The
+			 * easiest way to do that is make 'bar' on side1 not
+			 * be considered a colliding file but the other part
+			 * of a normal rename.  If the file is very different,
+			 * well we're going to get content merge conflicts
+			 * anyway so it doesn't hurt.  And if the colliding
+			 * file also has a different type, that'll be handled
+			 * by the content merge logic in process_entry() too.
+			 *
+			 * See also t6430, 'rename vs. rename/symlink'
+			 */
+			collision = 0;
+		}
+		if (source_deleted) {
+			if (target_index == 1) {
+				rename_branch = opt->branch1;
+				delete_branch = opt->branch2;
+			} else {
+				rename_branch = opt->branch2;
+				delete_branch = opt->branch1;
+			}
+		}
+
+		assert(source_deleted || oldinfo->filemask & old_sidemask);
+
+		/* Need to check for special types of rename conflicts... */
+		if (collision && !source_deleted) {
+			/* collision: rename/add or rename/rename(2to1) */
+			const char *pathnames[3];
+			struct version_info merged;
+
+			struct conflict_info *base, *side1, *side2;
+			unsigned clean;
+
+			pathnames[0] = oldpath;
+			pathnames[other_source_index] = oldpath;
+			pathnames[target_index] = newpath;
+
+			base = strmap_get(&opt->priv->paths, pathnames[0]);
+			side1 = strmap_get(&opt->priv->paths, pathnames[1]);
+			side2 = strmap_get(&opt->priv->paths, pathnames[2]);
+
+			VERIFY_CI(base);
+			VERIFY_CI(side1);
+			VERIFY_CI(side2);
+
+			clean = handle_content_merge(opt, pair->one->path,
+						     &base->stages[0],
+						     &side1->stages[1],
+						     &side2->stages[2],
+						     pathnames,
+						     1 + 2 * opt->priv->call_depth,
+						     &merged);
+
+			memcpy(&newinfo->stages[target_index], &merged,
+			       sizeof(merged));
+			if (!clean) {
+				path_msg(opt, newpath, 0,
+					 _("CONFLICT (rename involved in "
+					   "collision): rename of %s -> %s has "
+					   "content conflicts AND collides "
+					   "with another path; this may result "
+					   "in nested conflict markers."),
+					 oldpath, newpath);
+			}
+		} else if (collision && source_deleted) {
+			/*
+			 * rename/add/delete or rename/rename(2to1)/delete:
+			 * since oldpath was deleted on the side that didn't
+			 * do the rename, there's not much of a content merge
+			 * we can do for the rename.  oldinfo->merged.is_null
+			 * was already set, so we just leave things as-is so
+			 * they look like an add/add conflict.
+			 */
+
+			newinfo->path_conflict = 1;
+			path_msg(opt, newpath, 0,
+				 _("CONFLICT (rename/delete): %s renamed "
+				   "to %s in %s, but deleted in %s."),
+				 oldpath, newpath, rename_branch, delete_branch);
+		} else {
+			/*
+			 * a few different cases...start by copying the
+			 * existing stage(s) from oldinfo over the newinfo
+			 * and update the pathname(s).
+			 */
+			memcpy(&newinfo->stages[0], &oldinfo->stages[0],
+			       sizeof(newinfo->stages[0]));
+			newinfo->filemask |= (1 << MERGE_BASE);
+			newinfo->pathnames[0] = oldpath;
+			if (type_changed) {
+				/* rename vs. typechange */
+				/* Mark the original as resolved by removal */
+				memcpy(&oldinfo->stages[0].oid, &null_oid,
+				       sizeof(oldinfo->stages[0].oid));
+				oldinfo->stages[0].mode = 0;
+				oldinfo->filemask &= 0x06;
+			} else if (source_deleted) {
+				/* rename/delete */
+				newinfo->path_conflict = 1;
+				path_msg(opt, newpath, 0,
+					 _("CONFLICT (rename/delete): %s renamed"
+					   " to %s in %s, but deleted in %s."),
+					 oldpath, newpath,
+					 rename_branch, delete_branch);
+			} else {
+				/* normal rename */
+				memcpy(&newinfo->stages[other_source_index],
+				       &oldinfo->stages[other_source_index],
+				       sizeof(newinfo->stages[0]));
+				newinfo->filemask |= (1 << other_source_index);
+				newinfo->pathnames[other_source_index] = oldpath;
+			}
+		}
+
+		if (!type_changed) {
+			/* Mark the original as resolved by removal */
+			oldinfo->merged.is_null = 1;
+			oldinfo->merged.clean = 1;
+		}
+
+	}
+
+	return clean_merge;
+}
+
+static void resolve_diffpair_statuses(struct diff_queue_struct *q)
+{
+	/*
+	 * A simplified version of diff_resolve_rename_copy(); would probably
+	 * just use that function but it's static...
+	 */
+	int i;
+	struct diff_filepair *p;
+
+	for (i = 0; i < q->nr; ++i) {
+		p = q->queue[i];
+		p->status = 0; /* undecided */
+		if (!DIFF_FILE_VALID(p->one))
+			p->status = DIFF_STATUS_ADDED;
+		else if (!DIFF_FILE_VALID(p->two))
+			p->status = DIFF_STATUS_DELETED;
+		else if (DIFF_PAIR_RENAME(p))
+			p->status = DIFF_STATUS_RENAMED;
+	}
+}
+
+static int compare_pairs(const void *a_, const void *b_)
+{
+	const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+	const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+
+	return strcmp(a->one->path, b->one->path);
+}
+
+/* Call diffcore_rename() to compute which files have changed on given side */
+static void detect_regular_renames(struct merge_options *opt,
+				   unsigned side_index)
+{
+	struct diff_options diff_opts;
+	struct rename_info *renames = &opt->priv->renames;
+
+	repo_diff_setup(opt->repo, &diff_opts);
+	diff_opts.flags.recursive = 1;
+	diff_opts.flags.rename_empty = 0;
+	diff_opts.detect_rename = DIFF_DETECT_RENAME;
+	diff_opts.rename_limit = opt->rename_limit;
+	if (opt->rename_limit <= 0)
+		diff_opts.rename_limit = 1000;
+	diff_opts.rename_score = opt->rename_score;
+	diff_opts.show_rename_progress = opt->show_rename_progress;
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	diff_setup_done(&diff_opts);
+
+	diff_queued_diff = renames->pairs[side_index];
+	trace2_region_enter("diff", "diffcore_rename", opt->repo);
+	diffcore_rename(&diff_opts);
+	trace2_region_leave("diff", "diffcore_rename", opt->repo);
+	resolve_diffpair_statuses(&diff_queued_diff);
+
+	if (diff_opts.needed_rename_limit > renames->needed_limit)
+		renames->needed_limit = diff_opts.needed_rename_limit;
+
+	renames->pairs[side_index] = diff_queued_diff;
+
+	diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+	diff_queued_diff.nr = 0;
+	diff_queued_diff.queue = NULL;
+	diff_flush(&diff_opts);
+}
+
+/*
+ * Get information of all renames which occurred in 'side_pairs', discarding
+ * non-renames.
+ */
+static int collect_renames(struct merge_options *opt,
+			   struct diff_queue_struct *result,
+			   unsigned side_index,
+			   struct strmap *dir_renames_for_side,
+			   struct strmap *rename_exclusions)
+{
+	int i, clean = 1;
+	struct strmap collisions;
+	struct diff_queue_struct *side_pairs;
+	struct hashmap_iter iter;
+	struct strmap_entry *entry;
+	struct rename_info *renames = &opt->priv->renames;
+
+	side_pairs = &renames->pairs[side_index];
+	compute_collisions(&collisions, dir_renames_for_side, side_pairs);
+
+	for (i = 0; i < side_pairs->nr; ++i) {
+		struct diff_filepair *p = side_pairs->queue[i];
+		char *new_path; /* non-NULL only with directory renames */
+
+		if (p->status != 'A' && p->status != 'R') {
+			diff_free_filepair(p);
+			continue;
+		}
+
+		new_path = check_for_directory_rename(opt, p->two->path,
+						      side_index,
+						      dir_renames_for_side,
+						      rename_exclusions,
+						      &collisions,
+						      &clean);
+
+		if (p->status != 'R' && !new_path) {
+			diff_free_filepair(p);
+			continue;
+		}
+
+		if (new_path)
+			apply_directory_rename_modifications(opt, p, new_path);
+
+		/*
+		 * p->score comes back from diffcore_rename_extended() with
+		 * the similarity of the renamed file.  The similarity is
+		 * was used to determine that the two files were related
+		 * and are a rename, which we have already used, but beyond
+		 * that we have no use for the similarity.  So p->score is
+		 * now irrelevant.  However, process_renames() will need to
+		 * know which side of the merge this rename was associated
+		 * with, so overwrite p->score with that value.
+		 */
+		p->score = side_index;
+		result->queue[result->nr++] = p;
+	}
+
+	/* Free each value in the collisions map */
+	strmap_for_each_entry(&collisions, &iter, entry) {
+		struct collision_info *info = entry->value;
+		string_list_clear(&info->source_files, 0);
+	}
+	/*
+	 * In compute_collisions(), we set collisions.strdup_strings to 0
+	 * so that we wouldn't have to make another copy of the new_path
+	 * allocated by apply_dir_rename().  But now that we've used them
+	 * and have no other references to these strings, it is time to
+	 * deallocate them.
+	 */
+	free_strmap_strings(&collisions);
+	strmap_clear(&collisions, 1);
+	return clean;
+}
+
+static int detect_and_process_renames(struct merge_options *opt,
+				      struct tree *merge_base,
+				      struct tree *side1,
+				      struct tree *side2)
+{
+	struct diff_queue_struct combined;
+	struct rename_info *renames = &opt->priv->renames;
+	int need_dir_renames, s, clean = 1;
+
+	memset(&combined, 0, sizeof(combined));
+
+	trace2_region_enter("merge", "regular renames", opt->repo);
+	detect_regular_renames(opt, MERGE_SIDE1);
+	detect_regular_renames(opt, MERGE_SIDE2);
+	trace2_region_leave("merge", "regular renames", opt->repo);
+
+	trace2_region_enter("merge", "directory renames", opt->repo);
+	need_dir_renames =
+	  !opt->priv->call_depth &&
+	  (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE ||
+	   opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT);
+
+	if (need_dir_renames) {
+		get_provisional_directory_renames(opt, MERGE_SIDE1, &clean);
+		get_provisional_directory_renames(opt, MERGE_SIDE2, &clean);
+		handle_directory_level_conflicts(opt);
+	}
+
+	ALLOC_GROW(combined.queue,
+		   renames->pairs[1].nr + renames->pairs[2].nr,
+		   combined.alloc);
+	clean &= collect_renames(opt, &combined, MERGE_SIDE1,
+				 &renames->dir_renames[2],
+				 &renames->dir_renames[1]);
+	clean &= collect_renames(opt, &combined, MERGE_SIDE2,
+				 &renames->dir_renames[1],
+				 &renames->dir_renames[2]);
+	QSORT(combined.queue, combined.nr, compare_pairs);
+	trace2_region_leave("merge", "directory renames", opt->repo);
+
+	trace2_region_enter("merge", "process renames", opt->repo);
+	clean &= process_renames(opt, &combined);
+	trace2_region_leave("merge", "process renames", opt->repo);
+
+	/* Free memory for renames->pairs[] and combined */
+	for (s = MERGE_SIDE1; s <= MERGE_SIDE2; s++) {
+		free(renames->pairs[s].queue);
+		DIFF_QUEUE_CLEAR(&renames->pairs[s]);
+	}
+	if (combined.nr) {
+		int i;
+		for (i = 0; i < combined.nr; i++)
+			diff_free_filepair(combined.queue[i]);
+		free(combined.queue);
+	}
+
+	return clean;
+}
+
+/*** Function Grouping: functions related to process_entries() ***/
+
+static int string_list_df_name_compare(const char *one, const char *two)
+{
+	int onelen = strlen(one);
+	int twolen = strlen(two);
+	/*
+	 * Here we only care that entries for D/F conflicts are
+	 * adjacent, in particular with the file of the D/F conflict
+	 * appearing before files below the corresponding directory.
+	 * The order of the rest of the list is irrelevant for us.
+	 *
+	 * To achieve this, we sort with df_name_compare and provide
+	 * the mode S_IFDIR so that D/F conflicts will sort correctly.
+	 * We use the mode S_IFDIR for everything else for simplicity,
+	 * since in other cases any changes in their order due to
+	 * sorting cause no problems for us.
+	 */
+	int cmp = df_name_compare(one, onelen, S_IFDIR,
+				  two, twolen, S_IFDIR);
+	/*
+	 * Now that 'foo' and 'foo/bar' compare equal, we have to make sure
+	 * that 'foo' comes before 'foo/bar'.
+	 */
+	if (cmp)
+		return cmp;
+	return onelen - twolen;
+}
+
+struct directory_versions {
+	/*
+	 * versions: list of (basename -> version_info)
+	 *
+	 * The basenames are in reverse lexicographic order of full pathnames,
+	 * as processed in process_entries().  This puts all entries within
+	 * a directory together, and covers the directory itself after
+	 * everything within it, allowing us to write subtrees before needing
+	 * to record information for the tree itself.
+	 */
+	struct string_list versions;
+
+	/*
+	 * offsets: list of (full relative path directories -> integer offsets)
+	 *
+	 * Since versions contains basenames from files in multiple different
+	 * directories, we need to know which entries in versions correspond
+	 * to which directories.  Values of e.g.
+	 *     ""             0
+	 *     src            2
+	 *     src/moduleA    5
+	 * Would mean that entries 0-1 of versions are files in the toplevel
+	 * directory, entries 2-4 are files under src/, and the remaining
+	 * entries starting at index 5 are files under src/moduleA/.
+	 */
+	struct string_list offsets;
+
+	/*
+	 * last_directory: directory that previously processed file found in
+	 *
+	 * last_directory starts NULL, but records the directory in which the
+	 * previous file was found within.  As soon as
+	 *    directory(current_file) != last_directory
+	 * then we need to start updating accounting in versions & offsets.
+	 * Note that last_directory is always the last path in "offsets" (or
+	 * NULL if "offsets" is empty) so this exists just for quick access.
+	 */
+	const char *last_directory;
+
+	/* last_directory_len: cached computation of strlen(last_directory) */
+	unsigned last_directory_len;
+};
+
+static int tree_entry_order(const void *a_, const void *b_)
+{
+	const struct string_list_item *a = a_;
+	const struct string_list_item *b = b_;
+
+	const struct merged_info *ami = a->util;
+	const struct merged_info *bmi = b->util;
+	return base_name_compare(a->string, strlen(a->string), ami->result.mode,
+				 b->string, strlen(b->string), bmi->result.mode);
+}
+
+static void write_tree(struct object_id *result_oid,
+		       struct string_list *versions,
+		       unsigned int offset,
+		       size_t hash_size)
+{
+	size_t maxlen = 0, extra;
+	unsigned int nr = versions->nr - offset;
+	struct strbuf buf = STRBUF_INIT;
+	struct string_list relevant_entries = STRING_LIST_INIT_NODUP;
+	int i;
+
+	/*
+	 * We want to sort the last (versions->nr-offset) entries in versions.
+	 * Do so by abusing the string_list API a bit: make another string_list
+	 * that contains just those entries and then sort them.
+	 *
+	 * We won't use relevant_entries again and will let it just pop off the
+	 * stack, so there won't be allocation worries or anything.
+	 */
+	relevant_entries.items = versions->items + offset;
+	relevant_entries.nr = versions->nr - offset;
+	QSORT(relevant_entries.items, relevant_entries.nr, tree_entry_order);
+
+	/* Pre-allocate some space in buf */
+	extra = hash_size + 8; /* 8: 6 for mode, 1 for space, 1 for NUL char */
+	for (i = 0; i < nr; i++) {
+		maxlen += strlen(versions->items[offset+i].string) + extra;
+	}
+	strbuf_grow(&buf, maxlen);
+
+	/* Write each entry out to buf */
+	for (i = 0; i < nr; i++) {
+		struct merged_info *mi = versions->items[offset+i].util;
+		struct version_info *ri = &mi->result;
+		strbuf_addf(&buf, "%o %s%c",
+			    ri->mode,
+			    versions->items[offset+i].string, '\0');
+		strbuf_add(&buf, ri->oid.hash, hash_size);
+	}
+
+	/* Write this object file out, and record in result_oid */
+	write_object_file(buf.buf, buf.len, tree_type, result_oid);
+	strbuf_release(&buf);
+}
+
+static void record_entry_for_tree(struct directory_versions *dir_metadata,
+				  const char *path,
+				  struct merged_info *mi)
+{
+	const char *basename;
+
+	if (mi->is_null)
+		/* nothing to record */
+		return;
+
+	basename = path + mi->basename_offset;
+	assert(strchr(basename, '/') == NULL);
+	string_list_append(&dir_metadata->versions,
+			   basename)->util = &mi->result;
+}
+
+static void write_completed_directory(struct merge_options *opt,
+				      const char *new_directory_name,
+				      struct directory_versions *info)
+{
+	const char *prev_dir;
+	struct merged_info *dir_info = NULL;
+	unsigned int offset;
+
+	/*
+	 * Some explanation of info->versions and info->offsets...
+	 *
+	 * process_entries() iterates over all relevant files AND
+	 * directories in reverse lexicographic order, and calls this
+	 * function.  Thus, an example of the paths that process_entries()
+	 * could operate on (along with the directories for those paths
+	 * being shown) is:
+	 *
+	 *     xtract.c             ""
+	 *     tokens.txt           ""
+	 *     src/moduleB/umm.c    src/moduleB
+	 *     src/moduleB/stuff.h  src/moduleB
+	 *     src/moduleB/baz.c    src/moduleB
+	 *     src/moduleB          src
+	 *     src/moduleA/foo.c    src/moduleA
+	 *     src/moduleA/bar.c    src/moduleA
+	 *     src/moduleA          src
+	 *     src                  ""
+	 *     Makefile             ""
+	 *
+	 * info->versions:
+	 *
+	 *     always contains the unprocessed entries and their
+	 *     version_info information.  For example, after the first five
+	 *     entries above, info->versions would be:
+	 *
+	 *     	   xtract.c     <xtract.c's version_info>
+	 *     	   token.txt    <token.txt's version_info>
+	 *     	   umm.c        <src/moduleB/umm.c's version_info>
+	 *     	   stuff.h      <src/moduleB/stuff.h's version_info>
+	 *     	   baz.c        <src/moduleB/baz.c's version_info>
+	 *
+	 *     Once a subdirectory is completed we remove the entries in
+	 *     that subdirectory from info->versions, writing it as a tree
+	 *     (write_tree()).  Thus, as soon as we get to src/moduleB,
+	 *     info->versions would be updated to
+	 *
+	 *     	   xtract.c     <xtract.c's version_info>
+	 *     	   token.txt    <token.txt's version_info>
+	 *     	   moduleB      <src/moduleB's version_info>
+	 *
+	 * info->offsets:
+	 *
+	 *     helps us track which entries in info->versions correspond to
+	 *     which directories.  When we are N directories deep (e.g. 4
+	 *     for src/modA/submod/subdir/), we have up to N+1 unprocessed
+	 *     directories (+1 because of toplevel dir).  Corresponding to
+	 *     the info->versions example above, after processing five entries
+	 *     info->offsets will be:
+	 *
+	 *     	   ""           0
+	 *     	   src/moduleB  2
+	 *
+	 *     which is used to know that xtract.c & token.txt are from the
+	 *     toplevel dirctory, while umm.c & stuff.h & baz.c are from the
+	 *     src/moduleB directory.  Again, following the example above,
+	 *     once we need to process src/moduleB, then info->offsets is
+	 *     updated to
+	 *
+	 *     	   ""           0
+	 *     	   src          2
+	 *
+	 *     which says that moduleB (and only moduleB so far) is in the
+	 *     src directory.
+	 *
+	 *     One unique thing to note about info->offsets here is that
+	 *     "src" was not added to info->offsets until there was a path
+	 *     (a file OR directory) immediately below src/ that got
+	 *     processed.
+	 *
+	 * Since process_entry() just appends new entries to info->versions,
+	 * write_completed_directory() only needs to do work if the next path
+	 * is in a directory that is different than the last directory found
+	 * in info->offsets.
+	 */
+
+	/*
+	 * If we are working with the same directory as the last entry, there
+	 * is no work to do.  (See comments above the directory_name member of
+	 * struct merged_info for why we can use pointer comparison instead of
+	 * strcmp here.)
+	 */
+	if (new_directory_name == info->last_directory)
+		return;
+
+	/*
+	 * If we are just starting (last_directory is NULL), or last_directory
+	 * is a prefix of the current directory, then we can just update
+	 * info->offsets to record the offset where we started this directory
+	 * and update last_directory to have quick access to it.
+	 */
+	if (info->last_directory == NULL ||
+	    !strncmp(new_directory_name, info->last_directory,
+		     info->last_directory_len)) {
+		uintptr_t offset = info->versions.nr;
+
+		info->last_directory = new_directory_name;
+		info->last_directory_len = strlen(info->last_directory);
+		/*
+		 * Record the offset into info->versions where we will
+		 * start recording basenames of paths found within
+		 * new_directory_name.
+		 */
+		string_list_append(&info->offsets,
+				   info->last_directory)->util = (void*)offset;
+		return;
+	}
+
+	/*
+	 * The next entry that will be processed will be within
+	 * new_directory_name.  Since at this point we know that
+	 * new_directory_name is within a different directory than
+	 * info->last_directory, we have all entries for info->last_directory
+	 * in info->versions and we need to create a tree object for them.
+	 */
+	dir_info = strmap_get(&opt->priv->paths, info->last_directory);
+	assert(dir_info);
+	offset = (uintptr_t)info->offsets.items[info->offsets.nr-1].util;
+	if (offset == info->versions.nr) {
+		/*
+		 * Actually, we don't need to create a tree object in this
+		 * case.  Whenever all files within a directory disappear
+		 * during the merge (e.g. unmodified on one side and
+		 * deleted on the other, or files were renamed elsewhere),
+		 * then we get here and the directory itself needs to be
+		 * omitted from its parent tree as well.
+		 */
+		dir_info->is_null = 1;
+	} else {
+		/*
+		 * Write out the tree to the git object directory, and also
+		 * record the mode and oid in dir_info->result.
+		 */
+		dir_info->is_null = 0;
+		dir_info->result.mode = S_IFDIR;
+		write_tree(&dir_info->result.oid, &info->versions, offset,
+			   opt->repo->hash_algo->rawsz);
+	}
+
+	/*
+	 * We've now used several entries from info->versions and one entry
+	 * from info->offsets, so we get rid of those values.
+	 */
+	info->offsets.nr--;
+	info->versions.nr = offset;
+
+	/*
+	 * Now we've taken care of the completed directory, but we need to
+	 * prepare things since future entries will be in
+	 * new_directory_name.  (In particular, process_entry() will be
+	 * appending new entries to info->versions.)  So, we need to make
+	 * sure new_directory_name is the last entry in info->offsets.
+	 */
+	prev_dir = info->offsets.nr == 0 ? NULL :
+		   info->offsets.items[info->offsets.nr-1].string;
+	if (new_directory_name != prev_dir) {
+		uintptr_t c = info->versions.nr;
+		string_list_append(&info->offsets,
+				   new_directory_name)->util = (void*)c;
+	}
+
+	/* And, of course, we need to update last_directory to match. */
+	info->last_directory = new_directory_name;
+	info->last_directory_len = strlen(info->last_directory);
+}
+
+/* Per entry merge function */
+static void process_entry(struct merge_options *opt,
+			  const char *path,
+			  struct conflict_info *ci,
+			  struct directory_versions *dir_metadata)
+{
+	int df_file_index = 0;
+
+	VERIFY_CI(ci);
+	assert(ci->filemask >= 0 && ci->filemask <= 7);
+	/* ci->match_mask == 7 was handled in collect_merge_info_callback() */
+	assert(ci->match_mask == 0 || ci->match_mask == 3 ||
+	       ci->match_mask == 5 || ci->match_mask == 6);
+
+	if (ci->dirmask) {
+		record_entry_for_tree(dir_metadata, path, &ci->merged);
+		if (ci->filemask == 0)
+			/* nothing else to handle */
+			return;
+		assert(ci->df_conflict);
+	}
+
+	if (ci->df_conflict && ci->merged.result.mode == 0) {
+		int i;
+
+		/*
+		 * directory no longer in the way, but we do have a file we
+		 * need to place here so we need to clean away the "directory
+		 * merges to nothing" result.
+		 */
+		ci->df_conflict = 0;
+		assert(ci->filemask != 0);
+		ci->merged.clean = 0;
+		ci->merged.is_null = 0;
+		/* and we want to zero out any directory-related entries */
+		ci->match_mask = (ci->match_mask & ~ci->dirmask);
+		ci->dirmask = 0;
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+			if (ci->filemask & (1 << i))
+				continue;
+			ci->stages[i].mode = 0;
+			oidcpy(&ci->stages[i].oid, &null_oid);
+		}
+	} else if (ci->df_conflict && ci->merged.result.mode != 0) {
+		/*
+		 * This started out as a D/F conflict, and the entries in
+		 * the competing directory were not removed by the merge as
+		 * evidenced by write_completed_directory() writing a value
+		 * to ci->merged.result.mode.
+		 */
+		struct conflict_info *new_ci;
+		const char *branch;
+		const char *old_path = path;
+		int i;
+
+		assert(ci->merged.result.mode == S_IFDIR);
+
+		/*
+		 * If filemask is 1, we can just ignore the file as having
+		 * been deleted on both sides.  We do not want to overwrite
+		 * ci->merged.result, since it stores the tree for all the
+		 * files under it.
+		 */
+		if (ci->filemask == 1) {
+			ci->filemask = 0;
+			return;
+		}
+
+		/*
+		 * This file still exists on at least one side, and we want
+		 * the directory to remain here, so we need to move this
+		 * path to some new location.
+		 */
+		new_ci = xcalloc(1, sizeof(*new_ci));
+		/* We don't really want new_ci->merged.result copied, but it'll
+		 * be overwritten below so it doesn't matter.  We also don't
+		 * want any directory mode/oid values copied, but we'll zero
+		 * those out immediately.  We do want the rest of ci copied.
+		 */
+		memcpy(new_ci, ci, sizeof(*ci));
+		new_ci->match_mask = (new_ci->match_mask & ~new_ci->dirmask);
+		new_ci->dirmask = 0;
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+			if (new_ci->filemask & (1 << i))
+				continue;
+			/* zero out any entries related to directories */
+			new_ci->stages[i].mode = 0;
+			oidcpy(&new_ci->stages[i].oid, &null_oid);
+		}
+
+		/*
+		 * Find out which side this file came from; note that we
+		 * cannot just use ci->filemask, because renames could cause
+		 * the filemask to go back to 7.  So we use dirmask, then
+		 * pick the opposite side's index.
+		 */
+		df_file_index = (ci->dirmask & (1 << 1)) ? 2 : 1;
+		branch = (df_file_index == 1) ? opt->branch1 : opt->branch2;
+		path = unique_path(&opt->priv->paths, path, branch);
+		strmap_put(&opt->priv->paths, path, new_ci);
+
+		path_msg(opt, path, 0,
+			 _("CONFLICT (file/directory): directory in the way "
+			   "of %s from %s; moving it to %s instead."),
+			 old_path, branch, path);
+
+		/*
+		 * Zero out the filemask for the old ci.  At this point, ci
+		 * was just an entry for a directory, so we don't need to
+		 * do anything more with it.
+		 */
+		ci->filemask = 0;
+
+		/*
+		 * Now note that we're working on the new entry (path was
+		 * updated above.
+		 */
+		ci = new_ci;
+	}
+
+	/*
+	 * NOTE: Below there is a long switch-like if-elseif-elseif... block
+	 *       which the code goes through even for the df_conflict cases
+	 *       above.
+	 */
+	if (ci->match_mask) {
+		ci->merged.clean = 1;
+		if (ci->match_mask == 6) {
+			/* stages[1] == stages[2] */
+			ci->merged.result.mode = ci->stages[1].mode;
+			oidcpy(&ci->merged.result.oid, &ci->stages[1].oid);
+		} else {
+			/* determine the mask of the side that didn't match */
+			unsigned int othermask = 7 & ~ci->match_mask;
+			int side = (othermask == 4) ? 2 : 1;
+
+			ci->merged.result.mode = ci->stages[side].mode;
+			ci->merged.is_null = !ci->merged.result.mode;
+			oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
+
+			assert(othermask == 2 || othermask == 4);
+			assert(ci->merged.is_null ==
+			       (ci->filemask == ci->match_mask));
+		}
+	} else if (ci->filemask >= 6 &&
+		   (S_IFMT & ci->stages[1].mode) !=
+		   (S_IFMT & ci->stages[2].mode)) {
+		/* Two different items from (file/submodule/symlink) */
+		if (opt->priv->call_depth) {
+			/* Just use the version from the merge base */
+			ci->merged.clean = 0;
+			oidcpy(&ci->merged.result.oid, &ci->stages[0].oid);
+			ci->merged.result.mode = ci->stages[0].mode;
+			ci->merged.is_null = (ci->merged.result.mode == 0);
+		} else {
+			/* Handle by renaming one or both to separate paths. */
+			unsigned o_mode = ci->stages[0].mode;
+			unsigned a_mode = ci->stages[1].mode;
+			unsigned b_mode = ci->stages[2].mode;
+			struct conflict_info *new_ci;
+			const char *a_path = NULL, *b_path = NULL;
+			int rename_a = 0, rename_b = 0;
+
+			new_ci = xmalloc(sizeof(*new_ci));
+
+			if (S_ISREG(a_mode))
+				rename_a = 1;
+			else if (S_ISREG(b_mode))
+				rename_b = 1;
+			else {
+				rename_a = 1;
+				rename_b = 1;
+			}
+
+			path_msg(opt, path, 0,
+				 _("CONFLICT (distinct types): %s had different "
+				   "types on each side; renamed %s of them so "
+				   "each can be recorded somewhere."),
+				 path,
+				 (rename_a && rename_b) ? _("both") : _("one"));
+
+			ci->merged.clean = 0;
+			memcpy(new_ci, ci, sizeof(*new_ci));
+
+			/* Put b into new_ci, removing a from stages */
+			new_ci->merged.result.mode = ci->stages[2].mode;
+			oidcpy(&new_ci->merged.result.oid, &ci->stages[2].oid);
+			new_ci->stages[1].mode = 0;
+			oidcpy(&new_ci->stages[1].oid, &null_oid);
+			new_ci->filemask = 5;
+			if ((S_IFMT & b_mode) != (S_IFMT & o_mode)) {
+				new_ci->stages[0].mode = 0;
+				oidcpy(&new_ci->stages[0].oid, &null_oid);
+				new_ci->filemask = 4;
+			}
+
+			/* Leave only a in ci, fixing stages. */
+			ci->merged.result.mode = ci->stages[1].mode;
+			oidcpy(&ci->merged.result.oid, &ci->stages[1].oid);
+			ci->stages[2].mode = 0;
+			oidcpy(&ci->stages[2].oid, &null_oid);
+			ci->filemask = 3;
+			if ((S_IFMT & a_mode) != (S_IFMT & o_mode)) {
+				ci->stages[0].mode = 0;
+				oidcpy(&ci->stages[0].oid, &null_oid);
+				ci->filemask = 2;
+			}
+
+			/* Insert entries into opt->priv_paths */
+			assert(rename_a || rename_b);
+			if (rename_a) {
+				a_path = unique_path(&opt->priv->paths,
+						     path, opt->branch1);
+				strmap_put(&opt->priv->paths, a_path, ci);
+			}
+
+			if (rename_b)
+				b_path = unique_path(&opt->priv->paths,
+						     path, opt->branch2);
+			else
+				b_path = path;
+			strmap_put(&opt->priv->paths, b_path, new_ci);
+
+			if (rename_a && rename_b) {
+				strmap_remove(&opt->priv->paths, path, 0);
+				/*
+				 * We removed path from opt->priv->paths.  path
+				 * will also eventually need to be freed, but
+				 * it may still be used by e.g.  ci->pathnames.
+				 * So, store it in another string-list for now.
+				 */
+				string_list_append(&opt->priv->paths_to_free,
+						   path);
+			}
+
+			/*
+			 * Do special handling for b_path since process_entry()
+			 * won't be called on it specially.
+			 */
+			strmap_put(&opt->priv->conflicted, b_path, new_ci);
+			record_entry_for_tree(dir_metadata, b_path,
+					      &new_ci->merged);
+
+			/*
+			 * Remaining code for processing this entry should
+			 * think in terms of processing a_path.
+			 */
+			if (a_path)
+				path = a_path;
+		}
+	} else if (ci->filemask >= 6) {
+		/* Need a two-way or three-way content merge */
+		struct version_info merged_file;
+		unsigned clean_merge;
+		struct version_info *o = &ci->stages[0];
+		struct version_info *a = &ci->stages[1];
+		struct version_info *b = &ci->stages[2];
+
+		clean_merge = handle_content_merge(opt, path, o, a, b,
+						   ci->pathnames,
+						   opt->priv->call_depth * 2,
+						   &merged_file);
+		ci->merged.clean = clean_merge &&
+				   !ci->df_conflict && !ci->path_conflict;
+		ci->merged.result.mode = merged_file.mode;
+		ci->merged.is_null = (merged_file.mode == 0);
+		oidcpy(&ci->merged.result.oid, &merged_file.oid);
+		if (clean_merge && ci->df_conflict) {
+			assert(df_file_index == 1 || df_file_index == 2);
+			ci->filemask = 1 << df_file_index;
+			ci->stages[df_file_index].mode = merged_file.mode;
+			oidcpy(&ci->stages[df_file_index].oid, &merged_file.oid);
+		}
+		if (!clean_merge) {
+			const char *reason = _("content");
+			if (ci->filemask == 6)
+				reason = _("add/add");
+			if (S_ISGITLINK(merged_file.mode))
+				reason = _("submodule");
+			path_msg(opt, path, 0,
+				 _("CONFLICT (%s): Merge conflict in %s"),
+				 reason, path);
+		}
+	} else if (ci->filemask == 3 || ci->filemask == 5) {
+		/* Modify/delete */
+		const char *modify_branch, *delete_branch;
+		int side = (ci->filemask == 5) ? 2 : 1;
+		int index = opt->priv->call_depth ? 0 : side;
+
+		ci->merged.result.mode = ci->stages[index].mode;
+		oidcpy(&ci->merged.result.oid, &ci->stages[index].oid);
+		ci->merged.clean = 0;
+
+		modify_branch = (side == 1) ? opt->branch1 : opt->branch2;
+		delete_branch = (side == 1) ? opt->branch2 : opt->branch1;
+
+		if (ci->path_conflict &&
+		    oideq(&ci->stages[0].oid, &ci->stages[side].oid)) {
+			/*
+			 * This came from a rename/delete; no action to take,
+			 * but avoid printing "modify/delete" conflict notice
+			 * since the contents were not modified.
+			 */
+		} else {
+			path_msg(opt, path, 0,
+				 _("CONFLICT (modify/delete): %s deleted in %s "
+				   "and modified in %s.  Version %s of %s left "
+				   "in tree."),
+				 path, delete_branch, modify_branch,
+				 modify_branch, path);
+		}
+	} else if (ci->filemask == 2 || ci->filemask == 4) {
+		/* Added on one side */
+		int side = (ci->filemask == 4) ? 2 : 1;
+		ci->merged.result.mode = ci->stages[side].mode;
+		oidcpy(&ci->merged.result.oid, &ci->stages[side].oid);
+		ci->merged.clean = !ci->df_conflict && !ci->path_conflict;
+	} else if (ci->filemask == 1) {
+		/* Deleted on both sides */
+		ci->merged.is_null = 1;
+		ci->merged.result.mode = 0;
+		oidcpy(&ci->merged.result.oid, &null_oid);
+		ci->merged.clean = !ci->path_conflict;
+	}
+
+	/*
+	 * If still conflicted, record it separately.  This allows us to later
+	 * iterate over just conflicted entries when updating the index instead
+	 * of iterating over all entries.
+	 */
+	if (!ci->merged.clean)
+		strmap_put(&opt->priv->conflicted, path, ci);
+	record_entry_for_tree(dir_metadata, path, &ci->merged);
+}
+
+static void process_entries(struct merge_options *opt,
+			    struct object_id *result_oid)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *e;
+	struct string_list plist = STRING_LIST_INIT_NODUP;
+	struct string_list_item *entry;
+	struct directory_versions dir_metadata = { STRING_LIST_INIT_NODUP,
+						   STRING_LIST_INIT_NODUP,
+						   NULL, 0 };
+
+	trace2_region_enter("merge", "process_entries setup", opt->repo);
+	if (strmap_empty(&opt->priv->paths)) {
+		oidcpy(result_oid, opt->repo->hash_algo->empty_tree);
+		return;
+	}
+
+	/* Hack to pre-allocate plist to the desired size */
+	trace2_region_enter("merge", "plist grow", opt->repo);
+	ALLOC_GROW(plist.items, strmap_get_size(&opt->priv->paths), plist.alloc);
+	trace2_region_leave("merge", "plist grow", opt->repo);
+
+	/* Put every entry from paths into plist, then sort */
+	trace2_region_enter("merge", "plist copy", opt->repo);
+	strmap_for_each_entry(&opt->priv->paths, &iter, e) {
+		string_list_append(&plist, e->key)->util = e->value;
+	}
+	trace2_region_leave("merge", "plist copy", opt->repo);
+
+	trace2_region_enter("merge", "plist special sort", opt->repo);
+	plist.cmp = string_list_df_name_compare;
+	string_list_sort(&plist);
+	trace2_region_leave("merge", "plist special sort", opt->repo);
+
+	trace2_region_leave("merge", "process_entries setup", opt->repo);
+
+	/*
+	 * Iterate over the items in reverse order, so we can handle paths
+	 * below a directory before needing to handle the directory itself.
+	 *
+	 * This allows us to write subtrees before we need to write trees,
+	 * and it also enables sane handling of directory/file conflicts
+	 * (because it allows us to know whether the directory is still in
+	 * the way when it is time to process the file at the same path).
+	 */
+	trace2_region_enter("merge", "processing", opt->repo);
+	for (entry = &plist.items[plist.nr-1]; entry >= plist.items; --entry) {
+		char *path = entry->string;
+		/*
+		 * NOTE: mi may actually be a pointer to a conflict_info, but
+		 * we have to check mi->clean first to see if it's safe to
+		 * reassign to such a pointer type.
+		 */
+		struct merged_info *mi = entry->util;
+
+		write_completed_directory(opt, mi->directory_name,
+					  &dir_metadata);
+		if (mi->clean)
+			record_entry_for_tree(&dir_metadata, path, mi);
+		else {
+			struct conflict_info *ci = (struct conflict_info *)mi;
+			process_entry(opt, path, ci, &dir_metadata);
+		}
+	}
+	trace2_region_leave("merge", "processing", opt->repo);
+
+	trace2_region_enter("merge", "process_entries cleanup", opt->repo);
+	if (dir_metadata.offsets.nr != 1 ||
+	    (uintptr_t)dir_metadata.offsets.items[0].util != 0) {
+		printf("dir_metadata.offsets.nr = %d (should be 1)\n",
+		       dir_metadata.offsets.nr);
+		printf("dir_metadata.offsets.items[0].util = %u (should be 0)\n",
+		       (unsigned)(uintptr_t)dir_metadata.offsets.items[0].util);
+		fflush(stdout);
+		BUG("dir_metadata accounting completely off; shouldn't happen");
+	}
+	write_tree(result_oid, &dir_metadata.versions, 0,
+		   opt->repo->hash_algo->rawsz);
+	string_list_clear(&plist, 0);
+	string_list_clear(&dir_metadata.versions, 0);
+	string_list_clear(&dir_metadata.offsets, 0);
+	trace2_region_leave("merge", "process_entries cleanup", opt->repo);
+}
+
+/*** Function Grouping: functions related to merge_switch_to_result() ***/
+
+static int checkout(struct merge_options *opt,
+		    struct tree *prev,
+		    struct tree *next)
+{
+	/* Switch the index/working copy from old to new */
+	int ret;
+	struct tree_desc trees[2];
+	struct unpack_trees_options unpack_opts;
+
+	memset(&unpack_opts, 0, sizeof(unpack_opts));
+	unpack_opts.head_idx = -1;
+	unpack_opts.src_index = opt->repo->index;
+	unpack_opts.dst_index = opt->repo->index;
+
+	setup_unpack_trees_porcelain(&unpack_opts, "merge");
+
+	/*
+	 * NOTE: if this were just "git checkout" code, we would probably
+	 * read or refresh the cache and check for a conflicted index, but
+	 * builtin/merge.c or sequencer.c really needs to read the index
+	 * and check for conflicted entries before starting merging for a
+	 * good user experience (no sense waiting for merges/rebases before
+	 * erroring out), so there's no reason to duplicate that work here.
+	 */
+
+	/* 2-way merge to the new branch */
+	unpack_opts.update = 1;
+	unpack_opts.merge = 1;
+	unpack_opts.quiet = 0; /* FIXME: sequencer might want quiet? */
+	unpack_opts.verbose_update = (opt->verbosity > 2);
+	unpack_opts.fn = twoway_merge;
+	if (1/* FIXME: opts->overwrite_ignore*/) {
+		unpack_opts.dir = xcalloc(1, sizeof(*unpack_opts.dir));
+		unpack_opts.dir->flags |= DIR_SHOW_IGNORED;
+		setup_standard_excludes(unpack_opts.dir);
+	}
+	parse_tree(prev);
+	init_tree_desc(&trees[0], prev->buffer, prev->size);
+	parse_tree(next);
+	init_tree_desc(&trees[1], next->buffer, next->size);
+
+	ret = unpack_trees(2, trees, &unpack_opts);
+	clear_unpack_trees_porcelain(&unpack_opts);
+	dir_clear(unpack_opts.dir);
+	FREE_AND_NULL(unpack_opts.dir);
+	return ret;
+}
+
+static int record_conflicted_index_entries(struct merge_options *opt,
+					   struct index_state *index,
+					   struct strmap *paths,
+					   struct strmap *conflicted)
+{
+	struct hashmap_iter iter;
+	struct strmap_entry *e;
+	int errs = 0;
+	int original_cache_nr;
+
+	if (strmap_empty(conflicted))
+		return 0;
+
+	original_cache_nr = index->cache_nr;
+
+	/* Put every entry from paths into plist, then sort */
+	strmap_for_each_entry(conflicted, &iter, e) {
+		const char *path = e->key;
+		struct conflict_info *ci = e->value;
+		int pos;
+		struct cache_entry *ce;
+		int i;
+
+		VERIFY_CI(ci);
+
+		/*
+		 * The index will already have a stage=0 entry for this path,
+		 * because we created an as-merged-as-possible version of the
+		 * file and checkout() moved the working copy and index over
+		 * to that version.
+		 *
+		 * However, previous iterations through this loop will have
+		 * added unstaged entries to the end of the cache which
+		 * ignore the standard alphabetical ordering of cache
+		 * entries and break invariants needed for index_name_pos()
+		 * to work.  However, we know the entry we want is before
+		 * those appended cache entries, so do a temporary swap on
+		 * cache_nr to only look through entries of interest.
+		 */
+		SWAP(index->cache_nr, original_cache_nr);
+		pos = index_name_pos(index, path, strlen(path));
+		SWAP(index->cache_nr, original_cache_nr);
+		if (pos < 0) {
+			if (ci->filemask != 1)
+				BUG("Conflicted %s but nothing in basic working tree or index; this shouldn't happen", path);
+			cache_tree_invalidate_path(index, path);
+		} else {
+			ce = index->cache[pos];
+
+			/*
+			 * Clean paths with CE_SKIP_WORKTREE set will not be
+			 * written to the working tree by the unpack_trees()
+			 * call in checkout().  Our conflicted entries would
+			 * have appeared clean to that code since we ignored
+			 * the higher order stages.  Thus, we need override
+			 * the CE_SKIP_WORKTREE bit and manually write those
+			 * files to the working disk here.
+			 *
+			 * TODO: Implement this CE_SKIP_WORKTREE fixup.
+			 */
+
+			/*
+			 * Mark this cache entry for removal and instead add
+			 * new stage>0 entries corresponding to the
+			 * conflicts.  If there are many conflicted entries, we
+			 * want to avoid memmove'ing O(NM) entries by
+			 * inserting the new entries one at a time.  So,
+			 * instead, we just add the new cache entries to the
+			 * end (ignoring normal index requirements on sort
+			 * order) and sort the index once we're all done.
+			 */
+			ce->ce_flags |= CE_REMOVE;
+		}
+
+		for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+			struct version_info *vi;
+			if (!(ci->filemask & (1ul << i)))
+				continue;
+			vi = &ci->stages[i];
+			ce = make_cache_entry(index, vi->mode, &vi->oid,
+					      path, i+1, 0);
+			add_index_entry(index, ce, ADD_CACHE_JUST_APPEND);
+		}
+	}
+
+	/*
+	 * Remove the unused cache entries (and invalidate the relevant
+	 * cache-trees), then sort the index entries to get the conflicted
+	 * entries we added to the end into their right locations.
+	 */
+	remove_marked_cache_entries(index, 1);
+	QSORT(index->cache, index->cache_nr, cmp_cache_name_compare);
+
+	return errs;
+}
+
 void merge_switch_to_result(struct merge_options *opt,
 			    struct tree *head,
 			    struct merge_result *result,
 			    int update_worktree_and_index,
 			    int display_update_msgs)
 {
-	die("Not yet implemented");
+	assert(opt->priv == NULL);
+	if (result->clean >= 0 && update_worktree_and_index) {
+		struct merge_options_internal *opti = result->priv;
+
+		trace2_region_enter("merge", "checkout", opt->repo);
+		if (checkout(opt, head, result->tree)) {
+			/* failure to function */
+			result->clean = -1;
+			return;
+		}
+		trace2_region_leave("merge", "checkout", opt->repo);
+
+		trace2_region_enter("merge", "record_conflicted", opt->repo);
+		if (record_conflicted_index_entries(opt, opt->repo->index,
+						    &opti->paths,
+						    &opti->conflicted)) {
+			/* failure to function */
+			result->clean = -1;
+			return;
+		}
+		trace2_region_leave("merge", "record_conflicted", opt->repo);
+	}
+
+	if (display_update_msgs) {
+		struct merge_options_internal *opti = result->priv;
+		struct hashmap_iter iter;
+		struct strmap_entry *e;
+		struct string_list olist = STRING_LIST_INIT_NODUP;
+		int i;
+
+		trace2_region_enter("merge", "display messages", opt->repo);
+
+		/* Hack to pre-allocate olist to the desired size */
+		ALLOC_GROW(olist.items, strmap_get_size(&opti->output),
+			   olist.alloc);
+
+		/* Put every entry from output into olist, then sort */
+		strmap_for_each_entry(&opti->output, &iter, e) {
+			string_list_append(&olist, e->key)->util = e->value;
+		}
+		string_list_sort(&olist);
+
+		/* Iterate over the items, printing them */
+		for (i = 0; i < olist.nr; ++i) {
+			struct strbuf *sb = olist.items[i].util;
+
+			printf("%s", sb->buf);
+		}
+		string_list_clear(&olist, 0);
+
+		/* Also include needed rename limit adjustment now */
+		diff_warn_rename_limit("merge.renamelimit",
+				       opti->renames.needed_limit, 0);
+
+		trace2_region_leave("merge", "display messages", opt->repo);
+	}
+
 	merge_finalize(opt, result);
 }
 
 void merge_finalize(struct merge_options *opt,
 		    struct merge_result *result)
 {
-	die("Not yet implemented");
+	struct merge_options_internal *opti = result->priv;
+
+	assert(opt->priv == NULL);
+
+	clear_or_reinit_internal_opts(opti, 0);
+	FREE_AND_NULL(opti);
+}
+
+/*** Function Grouping: helper functions for merge_incore_*() ***/
+
+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)
+{
+	struct commit *commit = alloc_commit_node(repo);
+
+	set_merge_remote_desc(commit, comment, (struct object *)commit);
+	set_commit_tree(commit, tree);
+	commit->object.parsed = 1;
+	return commit;
+}
+
+static void merge_start(struct merge_options *opt, struct merge_result *result)
+{
+	struct rename_info *renames;
+	int i;
+
+	/* Sanity checks on opt */
+	trace2_region_enter("merge", "sanity checks", opt->repo);
+	assert(opt->repo);
+
+	assert(opt->branch1 && opt->branch2);
+
+	assert(opt->detect_directory_renames >= MERGE_DIRECTORY_RENAMES_NONE &&
+	       opt->detect_directory_renames <= MERGE_DIRECTORY_RENAMES_TRUE);
+	assert(opt->rename_limit >= -1);
+	assert(opt->rename_score >= 0 && opt->rename_score <= MAX_SCORE);
+	assert(opt->show_rename_progress >= 0 && opt->show_rename_progress <= 1);
+
+	assert(opt->xdl_opts >= 0);
+	assert(opt->recursive_variant >= MERGE_VARIANT_NORMAL &&
+	       opt->recursive_variant <= MERGE_VARIANT_THEIRS);
+
+	/*
+	 * detect_renames, verbosity, buffer_output, and obuf are ignored
+	 * fields that were used by "recursive" rather than "ort" -- but
+	 * sanity check them anyway.
+	 */
+	assert(opt->detect_renames >= -1 &&
+	       opt->detect_renames <= DIFF_DETECT_COPY);
+	assert(opt->verbosity >= 0 && opt->verbosity <= 5);
+	assert(opt->buffer_output <= 2);
+	assert(opt->obuf.len == 0);
+
+	assert(opt->priv == NULL);
+	if (result->priv) {
+		opt->priv = result->priv;
+		result->priv = NULL;
+		/*
+		 * opt->priv non-NULL means we had results from a previous
+		 * run; do a few sanity checks that user didn't mess with
+		 * it in an obvious fashion.
+		 */
+		assert(opt->priv->call_depth == 0);
+		assert(!opt->priv->toplevel_dir ||
+		       0 == strlen(opt->priv->toplevel_dir));
+	}
+	trace2_region_leave("merge", "sanity checks", opt->repo);
+
+	/* Default to histogram diff.  Actually, just hardcode it...for now. */
+	opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
+
+	/* Initialization of opt->priv, our internal merge data */
+	trace2_region_enter("merge", "allocate/init", opt->repo);
+	if (opt->priv) {
+		clear_or_reinit_internal_opts(opt->priv, 1);
+		trace2_region_leave("merge", "allocate/init", opt->repo);
+		return;
+	}
+	opt->priv = xcalloc(1, sizeof(*opt->priv));
+
+	/* Initialization of various renames fields */
+	renames = &opt->priv->renames;
+	for (i = MERGE_SIDE1; i <= MERGE_SIDE2; i++) {
+		strset_init_with_options(&renames->dirs_removed[i],
+					 NULL, 0);
+		strmap_init_with_options(&renames->dir_rename_count[i],
+					 NULL, 1);
+		strmap_init_with_options(&renames->dir_renames[i],
+					 NULL, 0);
+	}
+
+	/*
+	 * Although we initialize opt->priv->paths with strdup_strings=0,
+	 * that's just to avoid making yet another copy of an allocated
+	 * string.  Putting the entry into paths means we are taking
+	 * ownership, so we will later free it.  paths_to_free is similar.
+	 *
+	 * In contrast, conflicted just has a subset of keys from paths, so
+	 * we don't want to free those (it'd be a duplicate free).
+	 */
+	strmap_init_with_options(&opt->priv->paths, NULL, 0);
+	strmap_init_with_options(&opt->priv->conflicted, NULL, 0);
+	string_list_init(&opt->priv->paths_to_free, 0);
+
+	/*
+	 * keys & strbufs in output will sometimes need to outlive "paths",
+	 * so it will have a copy of relevant keys.  It's probably a small
+	 * subset of the overall paths that have special output.
+	 */
+	strmap_init(&opt->priv->output);
+
+	trace2_region_leave("merge", "allocate/init", opt->repo);
+}
+
+/*** Function Grouping: merge_incore_*() and their internal variants ***/
+
+/*
+ * Originally from merge_trees_internal(); heavily adapted, though.
+ */
+static void merge_ort_nonrecursive_internal(struct merge_options *opt,
+					    struct tree *merge_base,
+					    struct tree *side1,
+					    struct tree *side2,
+					    struct merge_result *result)
+{
+	struct object_id working_tree_oid;
+
+	trace2_region_enter("merge", "collect_merge_info", opt->repo);
+	if (collect_merge_info(opt, merge_base, side1, side2) != 0) {
+		/*
+		 * TRANSLATORS: The %s arguments are: 1) tree hash of a merge
+		 * base, and 2-3) the trees for the two trees we're merging.
+		 */
+		err(opt, _("collecting merge info failed for trees %s, %s, %s"),
+		    oid_to_hex(&merge_base->object.oid),
+		    oid_to_hex(&side1->object.oid),
+		    oid_to_hex(&side2->object.oid));
+		result->clean = -1;
+		return;
+	}
+	trace2_region_leave("merge", "collect_merge_info", opt->repo);
+
+	trace2_region_enter("merge", "renames", opt->repo);
+	result->clean = detect_and_process_renames(opt, merge_base,
+						   side1, side2);
+	trace2_region_leave("merge", "renames", opt->repo);
+
+	trace2_region_enter("merge", "process_entries", opt->repo);
+	process_entries(opt, &working_tree_oid);
+	trace2_region_leave("merge", "process_entries", opt->repo);
+
+	/* Set return values */
+	result->tree = parse_tree_indirect(&working_tree_oid);
+	/* existence of conflicted entries implies unclean */
+	result->clean &= strmap_empty(&opt->priv->conflicted);
+	if (!opt->priv->call_depth) {
+		result->priv = opt->priv;
+		opt->priv = NULL;
+	}
+}
+
+/*
+ * Originally from merge_recursive_internal(); somewhat adapted, though.
+ */
+static void merge_ort_internal(struct merge_options *opt,
+			       struct commit_list *merge_bases,
+			       struct commit *h1,
+			       struct commit *h2,
+			       struct merge_result *result)
+{
+	struct commit_list *iter;
+	struct commit *merged_merge_bases;
+	const char *ancestor_name;
+	struct strbuf merge_base_abbrev = STRBUF_INIT;
+
+	if (!merge_bases) {
+		merge_bases = get_merge_bases(h1, h2);
+		/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
+		merge_bases = reverse_commit_list(merge_bases);
+	}
+
+	merged_merge_bases = pop_commit(&merge_bases);
+	if (merged_merge_bases == NULL) {
+		/* if there is no common ancestor, use an empty tree */
+		struct tree *tree;
+
+		tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);
+		merged_merge_bases = make_virtual_commit(opt->repo, tree,
+							 "ancestor");
+		ancestor_name = "empty tree";
+	} else if (merge_bases) {
+		ancestor_name = "merged common ancestors";
+	} else {
+		strbuf_add_unique_abbrev(&merge_base_abbrev,
+					 &merged_merge_bases->object.oid,
+					 DEFAULT_ABBREV);
+		ancestor_name = merge_base_abbrev.buf;
+	}
+
+	for (iter = merge_bases; iter; iter = iter->next) {
+		const char *saved_b1, *saved_b2;
+		struct commit *prev = merged_merge_bases;
+
+		opt->priv->call_depth++;
+		/*
+		 * When the merge fails, the result contains files
+		 * with conflict markers. The cleanness flag is
+		 * ignored (unless indicating an error), it was never
+		 * actually used, as result of merge_trees has always
+		 * overwritten it: the committed "conflicts" were
+		 * already resolved.
+		 */
+		saved_b1 = opt->branch1;
+		saved_b2 = opt->branch2;
+		opt->branch1 = "Temporary merge branch 1";
+		opt->branch2 = "Temporary merge branch 2";
+		merge_ort_internal(opt, NULL, prev, iter->item, result);
+		if (result->clean < 0)
+			return;
+		opt->branch1 = saved_b1;
+		opt->branch2 = saved_b2;
+		opt->priv->call_depth--;
+
+		merged_merge_bases = make_virtual_commit(opt->repo,
+							 result->tree,
+							 "merged tree");
+		commit_list_insert(prev, &merged_merge_bases->parents);
+		commit_list_insert(iter->item,
+				   &merged_merge_bases->parents->next);
+
+		clear_or_reinit_internal_opts(opt->priv, 1);
+	}
+
+	opt->ancestor = ancestor_name;
+	merge_ort_nonrecursive_internal(opt,
+					repo_get_commit_tree(opt->repo,
+							     merged_merge_bases),
+					repo_get_commit_tree(opt->repo, h1),
+					repo_get_commit_tree(opt->repo, h2),
+					result);
+	strbuf_release(&merge_base_abbrev);
+	opt->ancestor = NULL;  /* avoid accidental re-use of opt->ancestor */
 }
 
 void merge_incore_nonrecursive(struct merge_options *opt,
@@ -39,7 +3520,15 @@
 			       struct tree *side2,
 			       struct merge_result *result)
 {
-	die("Not yet implemented");
+	trace2_region_enter("merge", "incore_nonrecursive", opt->repo);
+
+	trace2_region_enter("merge", "merge_start", opt->repo);
+	assert(opt->ancestor != NULL);
+	merge_start(opt, result);
+	trace2_region_leave("merge", "merge_start", opt->repo);
+
+	merge_ort_nonrecursive_internal(opt, merge_base, side1, side2, result);
+	trace2_region_leave("merge", "incore_nonrecursive", opt->repo);
 }
 
 void merge_incore_recursive(struct merge_options *opt,
@@ -48,5 +3537,15 @@
 			    struct commit *side2,
 			    struct merge_result *result)
 {
-	die("Not yet implemented");
+	trace2_region_enter("merge", "incore_recursive", opt->repo);
+
+	/* We set the ancestor label based on the merge_bases */
+	assert(opt->ancestor == NULL);
+
+	trace2_region_enter("merge", "merge_start", opt->repo);
+	merge_start(opt, result);
+	trace2_region_leave("merge", "merge_start", opt->repo);
+
+	merge_ort_internal(opt, merge_bases, side1, side2, result);
+	trace2_region_leave("merge", "incore_recursive", opt->repo);
 }
diff --git a/merge-ort.h b/merge-ort.h
index 74adcca..d53a0a3 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -7,7 +7,14 @@
 struct tree;
 
 struct merge_result {
-	/* Whether the merge is clean */
+	/*
+	 * Whether the merge is clean; possible values:
+	 *    1: clean
+	 *    0: not clean (merge conflicts)
+	 *   <0: operation aborted prematurely.  (object database
+	 *       unreadable, disk full, etc.)  Worktree may be left in an
+	 *       inconsistent state if operation failed near the end.
+	 */
 	int clean;
 
 	/*
@@ -27,6 +34,16 @@
 /*
  * rename-detecting three-way merge with recursive ancestor consolidation.
  * working tree and index are untouched.
+ *
+ * merge_bases will be consumed (emptied) so make a copy if you need it.
+ *
+ * NOTE: empirically, the recursive algorithm will perform better if you
+ *       pass the merge_bases in the order of oldest commit to the
+ *       newest[1][2].
+ *
+ *       [1] https://lore.kernel.org/git/nycvar.QRO.7.76.6.1907252055500.21907@tvgsbejvaqbjf.bet/
+ *       [2] commit 8918b0c9c2 ("merge-recur: try to merge older merge bases
+ *           first", 2006-08-09)
  */
 void merge_incore_recursive(struct merge_options *opt,
 			    struct commit_list *merge_bases,
diff --git a/merge-recursive.c b/merge-recursive.c
index f736a0f..b052974 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3517,17 +3517,6 @@
 	return clean;
 }
 
-static struct commit_list *reverse_commit_list(struct commit_list *list)
-{
-	struct commit_list *next = NULL, *current, *backup;
-	for (current = list; current; current = backup) {
-		backup = current->next;
-		current->next = next;
-		next = current;
-	}
-	return next;
-}
-
 /*
  * Merge the commits h1 and h2, returning a flag (int) indicating the
  * cleanness of the merge.  Also, if opt->priv->call_depth, create a
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index abc8ce4..96f6209 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -15,6 +15,17 @@
 				"$LOCAL" "$MERGED" "$REMOTE"
 		fi
 		;;
+	*vimdiff1)
+		"$merge_tool_path" -f -d \
+			-c 'echon "Resolve conflicts leftward then save. Use :cq to abort."' \
+			"$LOCAL" "$REMOTE"
+		ret="$?"
+		if test "$ret" -eq 0
+		then
+			cp -- "$LOCAL" "$MERGED"
+		fi
+		return "$ret"
+		;;
 	*vimdiff2)
 		"$merge_tool_path" -f -d -c 'wincmd l' \
 			"$LOCAL" "$MERGED" "$REMOTE"
@@ -52,7 +63,7 @@
 
 list_tool_variants () {
 	for prefix in '' g n; do
-		for suffix in '' 2 3; do
+		for suffix in '' 1 2 3; do
 			echo "${prefix}vimdiff${suffix}"
 		done
 	done
diff --git a/midx.c b/midx.c
index 9d41b9c..971faa8 100644
--- a/midx.c
+++ b/midx.c
@@ -5,12 +5,13 @@
 #include "lockfile.h"
 #include "packfile.h"
 #include "object-store.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "midx.h"
 #include "progress.h"
 #include "trace2.h"
 #include "run-command.h"
 #include "repository.h"
+#include "chunk-format.h"
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION 1
@@ -21,14 +22,12 @@
 #define MIDX_HEADER_SIZE 12
 #define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz)
 
-#define MIDX_MAX_CHUNKS 5
 #define MIDX_CHUNK_ALIGNMENT 4
 #define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
 #define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
 #define MIDX_CHUNKID_LARGEOFFSETS 0x4c4f4646 /* "LOFF" */
-#define MIDX_CHUNKLOOKUP_WIDTH (sizeof(uint32_t) + sizeof(uint64_t))
 #define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
 #define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
 #define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
@@ -53,6 +52,19 @@
 	return xstrfmt("%s/pack/multi-pack-index", object_dir);
 }
 
+static int midx_read_oid_fanout(const unsigned char *chunk_start,
+				size_t chunk_size, void *data)
+{
+	struct multi_pack_index *m = data;
+	m->chunk_oid_fanout = (uint32_t *)chunk_start;
+
+	if (chunk_size != 4 * 256) {
+		error(_("multi-pack-index OID fanout is of the wrong size"));
+		return 1;
+	}
+	return 0;
+}
+
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local)
 {
 	struct multi_pack_index *m = NULL;
@@ -64,6 +76,7 @@
 	char *midx_name = get_midx_filename(object_dir);
 	uint32_t i;
 	const char *cur_pack_name;
+	struct chunkfile *cf = NULL;
 
 	fd = git_open(midx_name);
 
@@ -113,58 +126,23 @@
 
 	m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
 
-	for (i = 0; i < m->num_chunks; i++) {
-		uint32_t chunk_id = get_be32(m->data + MIDX_HEADER_SIZE +
-					     MIDX_CHUNKLOOKUP_WIDTH * i);
-		uint64_t chunk_offset = get_be64(m->data + MIDX_HEADER_SIZE + 4 +
-						 MIDX_CHUNKLOOKUP_WIDTH * i);
+	cf = init_chunkfile(NULL);
 
-		if (chunk_offset >= m->data_len)
-			die(_("invalid chunk offset (too large)"));
+	if (read_table_of_contents(cf, m->data, midx_size,
+				   MIDX_HEADER_SIZE, m->num_chunks))
+		goto cleanup_fail;
 
-		switch (chunk_id) {
-			case MIDX_CHUNKID_PACKNAMES:
-				m->chunk_pack_names = m->data + chunk_offset;
-				break;
-
-			case MIDX_CHUNKID_OIDFANOUT:
-				m->chunk_oid_fanout = (uint32_t *)(m->data + chunk_offset);
-				break;
-
-			case MIDX_CHUNKID_OIDLOOKUP:
-				m->chunk_oid_lookup = m->data + chunk_offset;
-				break;
-
-			case MIDX_CHUNKID_OBJECTOFFSETS:
-				m->chunk_object_offsets = m->data + chunk_offset;
-				break;
-
-			case MIDX_CHUNKID_LARGEOFFSETS:
-				m->chunk_large_offsets = m->data + chunk_offset;
-				break;
-
-			case 0:
-				die(_("terminating multi-pack-index chunk id appears earlier than expected"));
-				break;
-
-			default:
-				/*
-				 * Do nothing on unrecognized chunks, allowing future
-				 * extensions to add optional chunks.
-				 */
-				break;
-		}
-	}
-
-	if (!m->chunk_pack_names)
+	if (pair_chunk(cf, MIDX_CHUNKID_PACKNAMES, &m->chunk_pack_names) == CHUNK_NOT_FOUND)
 		die(_("multi-pack-index missing required pack-name chunk"));
-	if (!m->chunk_oid_fanout)
+	if (read_chunk(cf, MIDX_CHUNKID_OIDFANOUT, midx_read_oid_fanout, m) == CHUNK_NOT_FOUND)
 		die(_("multi-pack-index missing required OID fanout chunk"));
-	if (!m->chunk_oid_lookup)
+	if (pair_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, &m->chunk_oid_lookup) == CHUNK_NOT_FOUND)
 		die(_("multi-pack-index missing required OID lookup chunk"));
-	if (!m->chunk_object_offsets)
+	if (pair_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, &m->chunk_object_offsets) == CHUNK_NOT_FOUND)
 		die(_("multi-pack-index missing required object offsets chunk"));
 
+	pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets);
+
 	m->num_objects = ntohl(m->chunk_oid_fanout[255]);
 
 	m->pack_names = xcalloc(m->num_packs, sizeof(*m->pack_names));
@@ -190,6 +168,7 @@
 cleanup_fail:
 	free(m);
 	free(midx_name);
+	free(cf);
 	if (midx_map)
 		munmap(midx_map, midx_size);
 	if (0 <= fd)
@@ -265,7 +244,7 @@
 	const unsigned char *offset_data;
 	uint32_t offset32;
 
-	offset_data = m->chunk_object_offsets + pos * MIDX_CHUNK_OFFSET_WIDTH;
+	offset_data = m->chunk_object_offsets + (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH;
 	offset32 = get_be32(offset_data + sizeof(uint32_t));
 
 	if (m->chunk_large_offsets && offset32 & MIDX_LARGE_OFFSET_NEEDED) {
@@ -281,7 +260,8 @@
 
 static uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos)
 {
-	return get_be32(m->chunk_object_offsets + pos * MIDX_CHUNK_OFFSET_WIDTH);
+	return get_be32(m->chunk_object_offsets +
+			(off_t)pos * MIDX_CHUNK_OFFSET_WIDTH);
 }
 
 static int nth_midxed_pack_entry(struct repository *r,
@@ -451,49 +431,56 @@
 	return strcmp(a->pack_name, b->pack_name);
 }
 
-struct pack_list {
+struct write_midx_context {
 	struct pack_info *info;
 	uint32_t nr;
 	uint32_t alloc;
 	struct multi_pack_index *m;
 	struct progress *progress;
 	unsigned pack_paths_checked;
+
+	struct pack_midx_entry *entries;
+	uint32_t entries_nr;
+
+	uint32_t *pack_perm;
+	unsigned large_offsets_needed:1;
+	uint32_t num_large_offsets;
 };
 
 static void add_pack_to_midx(const char *full_path, size_t full_path_len,
 			     const char *file_name, void *data)
 {
-	struct pack_list *packs = (struct pack_list *)data;
+	struct write_midx_context *ctx = data;
 
 	if (ends_with(file_name, ".idx")) {
-		display_progress(packs->progress, ++packs->pack_paths_checked);
-		if (packs->m && midx_contains_pack(packs->m, file_name))
+		display_progress(ctx->progress, ++ctx->pack_paths_checked);
+		if (ctx->m && midx_contains_pack(ctx->m, file_name))
 			return;
 
-		ALLOC_GROW(packs->info, packs->nr + 1, packs->alloc);
+		ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
 
-		packs->info[packs->nr].p = add_packed_git(full_path,
-							  full_path_len,
-							  0);
+		ctx->info[ctx->nr].p = add_packed_git(full_path,
+						      full_path_len,
+						      0);
 
-		if (!packs->info[packs->nr].p) {
+		if (!ctx->info[ctx->nr].p) {
 			warning(_("failed to add packfile '%s'"),
 				full_path);
 			return;
 		}
 
-		if (open_pack_index(packs->info[packs->nr].p)) {
+		if (open_pack_index(ctx->info[ctx->nr].p)) {
 			warning(_("failed to open pack-index '%s'"),
 				full_path);
-			close_pack(packs->info[packs->nr].p);
-			FREE_AND_NULL(packs->info[packs->nr].p);
+			close_pack(ctx->info[ctx->nr].p);
+			FREE_AND_NULL(ctx->info[ctx->nr].p);
 			return;
 		}
 
-		packs->info[packs->nr].pack_name = xstrdup(file_name);
-		packs->info[packs->nr].orig_pack_int_id = packs->nr;
-		packs->info[packs->nr].expired = 0;
-		packs->nr++;
+		ctx->info[ctx->nr].pack_name = xstrdup(file_name);
+		ctx->info[ctx->nr].orig_pack_int_id = ctx->nr;
+		ctx->info[ctx->nr].expired = 0;
+		ctx->nr++;
 	}
 }
 
@@ -643,27 +630,26 @@
 	return deduplicated_entries;
 }
 
-static size_t write_midx_pack_names(struct hashfile *f,
-				    struct pack_info *info,
-				    uint32_t num_packs)
+static int write_midx_pack_names(struct hashfile *f, void *data)
 {
+	struct write_midx_context *ctx = data;
 	uint32_t i;
 	unsigned char padding[MIDX_CHUNK_ALIGNMENT];
 	size_t written = 0;
 
-	for (i = 0; i < num_packs; i++) {
+	for (i = 0; i < ctx->nr; i++) {
 		size_t writelen;
 
-		if (info[i].expired)
+		if (ctx->info[i].expired)
 			continue;
 
-		if (i && strcmp(info[i].pack_name, info[i - 1].pack_name) <= 0)
+		if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0)
 			BUG("incorrect pack-file order: %s before %s",
-			    info[i - 1].pack_name,
-			    info[i].pack_name);
+			    ctx->info[i - 1].pack_name,
+			    ctx->info[i].pack_name);
 
-		writelen = strlen(info[i].pack_name) + 1;
-		hashwrite(f, info[i].pack_name, writelen);
+		writelen = strlen(ctx->info[i].pack_name) + 1;
+		hashwrite(f, ctx->info[i].pack_name, writelen);
 		written += writelen;
 	}
 
@@ -672,18 +658,17 @@
 	if (i < MIDX_CHUNK_ALIGNMENT) {
 		memset(padding, 0, sizeof(padding));
 		hashwrite(f, padding, i);
-		written += i;
 	}
 
-	return written;
+	return 0;
 }
 
-static size_t write_midx_oid_fanout(struct hashfile *f,
-				    struct pack_midx_entry *objects,
-				    uint32_t nr_objects)
+static int write_midx_oid_fanout(struct hashfile *f,
+				 void *data)
 {
-	struct pack_midx_entry *list = objects;
-	struct pack_midx_entry *last = objects + nr_objects;
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *last = ctx->entries + ctx->entries_nr;
 	uint32_t count = 0;
 	uint32_t i;
 
@@ -704,21 +689,21 @@
 		list = next;
 	}
 
-	return MIDX_CHUNK_FANOUT_SIZE;
+	return 0;
 }
 
-static size_t write_midx_oid_lookup(struct hashfile *f, unsigned char hash_len,
-				    struct pack_midx_entry *objects,
-				    uint32_t nr_objects)
+static int write_midx_oid_lookup(struct hashfile *f,
+				 void *data)
 {
-	struct pack_midx_entry *list = objects;
+	struct write_midx_context *ctx = data;
+	unsigned char hash_len = the_hash_algo->rawsz;
+	struct pack_midx_entry *list = ctx->entries;
 	uint32_t i;
-	size_t written = 0;
 
-	for (i = 0; i < nr_objects; i++) {
+	for (i = 0; i < ctx->entries_nr; i++) {
 		struct pack_midx_entry *obj = list++;
 
-		if (i < nr_objects - 1) {
+		if (i < ctx->entries_nr - 1) {
 			struct pack_midx_entry *next = list;
 			if (oidcmp(&obj->oid, &next->oid) >= 0)
 				BUG("OIDs not in order: %s >= %s",
@@ -727,50 +712,48 @@
 		}
 
 		hashwrite(f, obj->oid.hash, (int)hash_len);
-		written += hash_len;
 	}
 
-	return written;
+	return 0;
 }
 
-static size_t write_midx_object_offsets(struct hashfile *f, int large_offset_needed,
-					uint32_t *perm,
-					struct pack_midx_entry *objects, uint32_t nr_objects)
+static int write_midx_object_offsets(struct hashfile *f,
+				     void *data)
 {
-	struct pack_midx_entry *list = objects;
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
 	uint32_t i, nr_large_offset = 0;
-	size_t written = 0;
 
-	for (i = 0; i < nr_objects; i++) {
+	for (i = 0; i < ctx->entries_nr; i++) {
 		struct pack_midx_entry *obj = list++;
 
-		if (perm[obj->pack_int_id] == PACK_EXPIRED)
+		if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED)
 			BUG("object %s is in an expired pack with int-id %d",
 			    oid_to_hex(&obj->oid),
 			    obj->pack_int_id);
 
-		hashwrite_be32(f, perm[obj->pack_int_id]);
+		hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]);
 
-		if (large_offset_needed && obj->offset >> 31)
+		if (ctx->large_offsets_needed && obj->offset >> 31)
 			hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
-		else if (!large_offset_needed && obj->offset >> 32)
+		else if (!ctx->large_offsets_needed && obj->offset >> 32)
 			BUG("object %s requires a large offset (%"PRIx64") but the MIDX is not writing large offsets!",
 			    oid_to_hex(&obj->oid),
 			    obj->offset);
 		else
 			hashwrite_be32(f, (uint32_t)obj->offset);
-
-		written += MIDX_CHUNK_OFFSET_WIDTH;
 	}
 
-	return written;
+	return 0;
 }
 
-static size_t write_midx_large_offsets(struct hashfile *f, uint32_t nr_large_offset,
-				       struct pack_midx_entry *objects, uint32_t nr_objects)
+static int write_midx_large_offsets(struct hashfile *f,
+				    void *data)
 {
-	struct pack_midx_entry *list = objects, *end = objects + nr_objects;
-	size_t written = 0;
+	struct write_midx_context *ctx = data;
+	struct pack_midx_entry *list = ctx->entries;
+	struct pack_midx_entry *end = ctx->entries + ctx->entries_nr;
+	uint32_t nr_large_offset = ctx->num_large_offsets;
 
 	while (nr_large_offset) {
 		struct pack_midx_entry *obj;
@@ -785,34 +768,26 @@
 		if (!(offset >> 31))
 			continue;
 
-		written += hashwrite_be64(f, offset);
+		hashwrite_be64(f, offset);
 
 		nr_large_offset--;
 	}
 
-	return written;
+	return 0;
 }
 
 static int write_midx_internal(const char *object_dir, struct multi_pack_index *m,
 			       struct string_list *packs_to_drop, unsigned flags)
 {
-	unsigned char cur_chunk, num_chunks = 0;
 	char *midx_name;
 	uint32_t i;
 	struct hashfile *f = NULL;
 	struct lock_file lk;
-	struct pack_list packs;
-	uint32_t *pack_perm = NULL;
-	uint64_t written = 0;
-	uint32_t chunk_ids[MIDX_MAX_CHUNKS + 1];
-	uint64_t chunk_offsets[MIDX_MAX_CHUNKS + 1];
-	uint32_t nr_entries, num_large_offsets = 0;
-	struct pack_midx_entry *entries = NULL;
-	struct progress *progress = NULL;
-	int large_offsets_needed = 0;
+	struct write_midx_context ctx = { 0 };
 	int pack_name_concat_len = 0;
 	int dropped_packs = 0;
 	int result = 0;
+	struct chunkfile *cf;
 
 	midx_name = get_midx_filename(object_dir);
 	if (safe_create_leading_directories(midx_name))
@@ -820,61 +795,62 @@
 			  midx_name);
 
 	if (m)
-		packs.m = m;
+		ctx.m = m;
 	else
-		packs.m = load_multi_pack_index(object_dir, 1);
+		ctx.m = load_multi_pack_index(object_dir, 1);
 
-	packs.nr = 0;
-	packs.alloc = packs.m ? packs.m->num_packs : 16;
-	packs.info = NULL;
-	ALLOC_ARRAY(packs.info, packs.alloc);
+	ctx.nr = 0;
+	ctx.alloc = ctx.m ? ctx.m->num_packs : 16;
+	ctx.info = NULL;
+	ALLOC_ARRAY(ctx.info, ctx.alloc);
 
-	if (packs.m) {
-		for (i = 0; i < packs.m->num_packs; i++) {
-			ALLOC_GROW(packs.info, packs.nr + 1, packs.alloc);
+	if (ctx.m) {
+		for (i = 0; i < ctx.m->num_packs; i++) {
+			ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
 
-			packs.info[packs.nr].orig_pack_int_id = i;
-			packs.info[packs.nr].pack_name = xstrdup(packs.m->pack_names[i]);
-			packs.info[packs.nr].p = NULL;
-			packs.info[packs.nr].expired = 0;
-			packs.nr++;
+			ctx.info[ctx.nr].orig_pack_int_id = i;
+			ctx.info[ctx.nr].pack_name = xstrdup(ctx.m->pack_names[i]);
+			ctx.info[ctx.nr].p = NULL;
+			ctx.info[ctx.nr].expired = 0;
+			ctx.nr++;
 		}
 	}
 
-	packs.pack_paths_checked = 0;
+	ctx.pack_paths_checked = 0;
 	if (flags & MIDX_PROGRESS)
-		packs.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
+		ctx.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
 	else
-		packs.progress = NULL;
+		ctx.progress = NULL;
 
-	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &packs);
-	stop_progress(&packs.progress);
+	for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx);
+	stop_progress(&ctx.progress);
 
-	if (packs.m && packs.nr == packs.m->num_packs && !packs_to_drop)
+	if (ctx.m && ctx.nr == ctx.m->num_packs && !packs_to_drop)
 		goto cleanup;
 
-	entries = get_sorted_entries(packs.m, packs.info, packs.nr, &nr_entries);
+	ctx.entries = get_sorted_entries(ctx.m, ctx.info, ctx.nr, &ctx.entries_nr);
 
-	for (i = 0; i < nr_entries; i++) {
-		if (entries[i].offset > 0x7fffffff)
-			num_large_offsets++;
-		if (entries[i].offset > 0xffffffff)
-			large_offsets_needed = 1;
+	ctx.large_offsets_needed = 0;
+	for (i = 0; i < ctx.entries_nr; i++) {
+		if (ctx.entries[i].offset > 0x7fffffff)
+			ctx.num_large_offsets++;
+		if (ctx.entries[i].offset > 0xffffffff)
+			ctx.large_offsets_needed = 1;
 	}
 
-	QSORT(packs.info, packs.nr, pack_info_compare);
+	QSORT(ctx.info, ctx.nr, pack_info_compare);
 
 	if (packs_to_drop && packs_to_drop->nr) {
 		int drop_index = 0;
 		int missing_drops = 0;
 
-		for (i = 0; i < packs.nr && drop_index < packs_to_drop->nr; i++) {
-			int cmp = strcmp(packs.info[i].pack_name,
+		for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) {
+			int cmp = strcmp(ctx.info[i].pack_name,
 					 packs_to_drop->items[drop_index].string);
 
 			if (!cmp) {
 				drop_index++;
-				packs.info[i].expired = 1;
+				ctx.info[i].expired = 1;
 			} else if (cmp > 0) {
 				error(_("did not see pack-file %s to drop"),
 				      packs_to_drop->items[drop_index].string);
@@ -882,7 +858,7 @@
 				missing_drops++;
 				i--;
 			} else {
-				packs.info[i].expired = 0;
+				ctx.info[i].expired = 0;
 			}
 		}
 
@@ -898,19 +874,19 @@
 	 *
 	 * pack_perm[old_id] = new_id
 	 */
-	ALLOC_ARRAY(pack_perm, packs.nr);
-	for (i = 0; i < packs.nr; i++) {
-		if (packs.info[i].expired) {
+	ALLOC_ARRAY(ctx.pack_perm, ctx.nr);
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].expired) {
 			dropped_packs++;
-			pack_perm[packs.info[i].orig_pack_int_id] = PACK_EXPIRED;
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED;
 		} else {
-			pack_perm[packs.info[i].orig_pack_int_id] = i - dropped_packs;
+			ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs;
 		}
 	}
 
-	for (i = 0; i < packs.nr; i++) {
-		if (!packs.info[i].expired)
-			pack_name_concat_len += strlen(packs.info[i].pack_name) + 1;
+	for (i = 0; i < ctx.nr; i++) {
+		if (!ctx.info[i].expired)
+			pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
 	}
 
 	if (pack_name_concat_len % MIDX_CHUNK_ALIGNMENT)
@@ -921,123 +897,52 @@
 	f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
 	FREE_AND_NULL(midx_name);
 
-	if (packs.m)
-		close_midx(packs.m);
+	if (ctx.m)
+		close_midx(ctx.m);
 
-	cur_chunk = 0;
-	num_chunks = large_offsets_needed ? 5 : 4;
-
-	if (packs.nr - dropped_packs == 0) {
+	if (ctx.nr - dropped_packs == 0) {
 		error(_("no pack files to index."));
 		result = 1;
 		goto cleanup;
 	}
 
-	written = write_midx_header(f, num_chunks, packs.nr - dropped_packs);
+	cf = init_chunkfile(f);
 
-	chunk_ids[cur_chunk] = MIDX_CHUNKID_PACKNAMES;
-	chunk_offsets[cur_chunk] = written + (num_chunks + 1) * MIDX_CHUNKLOOKUP_WIDTH;
+	add_chunk(cf, MIDX_CHUNKID_PACKNAMES, pack_name_concat_len,
+		  write_midx_pack_names);
+	add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE,
+		  write_midx_oid_fanout);
+	add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP,
+		  (size_t)ctx.entries_nr * the_hash_algo->rawsz,
+		  write_midx_oid_lookup);
+	add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS,
+		  (size_t)ctx.entries_nr * MIDX_CHUNK_OFFSET_WIDTH,
+		  write_midx_object_offsets);
 
-	cur_chunk++;
-	chunk_ids[cur_chunk] = MIDX_CHUNKID_OIDFANOUT;
-	chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk - 1] + pack_name_concat_len;
+	if (ctx.large_offsets_needed)
+		add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS,
+			(size_t)ctx.num_large_offsets * MIDX_CHUNK_LARGE_OFFSET_WIDTH,
+			write_midx_large_offsets);
 
-	cur_chunk++;
-	chunk_ids[cur_chunk] = MIDX_CHUNKID_OIDLOOKUP;
-	chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk - 1] + MIDX_CHUNK_FANOUT_SIZE;
-
-	cur_chunk++;
-	chunk_ids[cur_chunk] = MIDX_CHUNKID_OBJECTOFFSETS;
-	chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk - 1] + nr_entries * the_hash_algo->rawsz;
-
-	cur_chunk++;
-	chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk - 1] + nr_entries * MIDX_CHUNK_OFFSET_WIDTH;
-	if (large_offsets_needed) {
-		chunk_ids[cur_chunk] = MIDX_CHUNKID_LARGEOFFSETS;
-
-		cur_chunk++;
-		chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk - 1] +
-					   num_large_offsets * MIDX_CHUNK_LARGE_OFFSET_WIDTH;
-	}
-
-	chunk_ids[cur_chunk] = 0;
-
-	for (i = 0; i <= num_chunks; i++) {
-		if (i && chunk_offsets[i] < chunk_offsets[i - 1])
-			BUG("incorrect chunk offsets: %"PRIu64" before %"PRIu64,
-			    chunk_offsets[i - 1],
-			    chunk_offsets[i]);
-
-		if (chunk_offsets[i] % MIDX_CHUNK_ALIGNMENT)
-			BUG("chunk offset %"PRIu64" is not properly aligned",
-			    chunk_offsets[i]);
-
-		hashwrite_be32(f, chunk_ids[i]);
-		hashwrite_be64(f, chunk_offsets[i]);
-
-		written += MIDX_CHUNKLOOKUP_WIDTH;
-	}
-
-	if (flags & MIDX_PROGRESS)
-		progress = start_delayed_progress(_("Writing chunks to multi-pack-index"),
-					  num_chunks);
-	for (i = 0; i < num_chunks; i++) {
-		if (written != chunk_offsets[i])
-			BUG("incorrect chunk offset (%"PRIu64" != %"PRIu64") for chunk id %"PRIx32,
-			    chunk_offsets[i],
-			    written,
-			    chunk_ids[i]);
-
-		switch (chunk_ids[i]) {
-			case MIDX_CHUNKID_PACKNAMES:
-				written += write_midx_pack_names(f, packs.info, packs.nr);
-				break;
-
-			case MIDX_CHUNKID_OIDFANOUT:
-				written += write_midx_oid_fanout(f, entries, nr_entries);
-				break;
-
-			case MIDX_CHUNKID_OIDLOOKUP:
-				written += write_midx_oid_lookup(f, the_hash_algo->rawsz, entries, nr_entries);
-				break;
-
-			case MIDX_CHUNKID_OBJECTOFFSETS:
-				written += write_midx_object_offsets(f, large_offsets_needed, pack_perm, entries, nr_entries);
-				break;
-
-			case MIDX_CHUNKID_LARGEOFFSETS:
-				written += write_midx_large_offsets(f, num_large_offsets, entries, nr_entries);
-				break;
-
-			default:
-				BUG("trying to write unknown chunk id %"PRIx32,
-				    chunk_ids[i]);
-		}
-
-		display_progress(progress, i + 1);
-	}
-	stop_progress(&progress);
-
-	if (written != chunk_offsets[num_chunks])
-		BUG("incorrect final offset %"PRIu64" != %"PRIu64,
-		    written,
-		    chunk_offsets[num_chunks]);
+	write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
+	write_chunkfile(cf, &ctx);
 
 	finalize_hashfile(f, NULL, CSUM_FSYNC | CSUM_HASH_IN_STREAM);
+	free_chunkfile(cf);
 	commit_lock_file(&lk);
 
 cleanup:
-	for (i = 0; i < packs.nr; i++) {
-		if (packs.info[i].p) {
-			close_pack(packs.info[i].p);
-			free(packs.info[i].p);
+	for (i = 0; i < ctx.nr; i++) {
+		if (ctx.info[i].p) {
+			close_pack(ctx.info[i].p);
+			free(ctx.info[i].p);
 		}
-		free(packs.info[i].pack_name);
+		free(ctx.info[i].pack_name);
 	}
 
-	free(packs.info);
-	free(entries);
-	free(pack_perm);
+	free(ctx.info);
+	free(ctx.entries);
+	free(ctx.pack_perm);
 	free(midx_name);
 	return result;
 }
diff --git a/name-hash.c b/name-hash.c
index 5d3c7b1..4e03fac 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -7,6 +7,7 @@
  */
 #include "cache.h"
 #include "thread-utils.h"
+#include "trace2.h"
 
 struct dir_entry {
 	struct hashmap_entry ent;
@@ -577,6 +578,7 @@
 	if (istate->name_hash_initialized)
 		return;
 	trace_performance_enter();
+	trace2_region_enter("index", "name-hash-init", istate->repo);
 	hashmap_init(&istate->name_hash, cache_entry_cmp, NULL, istate->cache_nr);
 	hashmap_init(&istate->dir_hash, dir_entry_cmp, NULL, istate->cache_nr);
 
@@ -597,6 +599,7 @@
 	}
 
 	istate->name_hash_initialized = 1;
+	trace2_region_leave("index", "name-hash-init", istate->repo);
 	trace_performance_leave("initialize name hash");
 }
 
diff --git a/sha1-file.c b/object-file.c
similarity index 99%
rename from sha1-file.c
rename to object-file.c
index c3c49d2..5bcfde8 100644
--- a/sha1-file.c
+++ b/object-file.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  *
- * This handles basic git sha1 object files - packing, unpacking,
+ * This handles basic git object files - packing, unpacking,
  * creation etc.
  */
 #include "cache.h"
@@ -20,7 +20,7 @@
 #include "tree-walk.h"
 #include "refs.h"
 #include "pack-revindex.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "bulk-checkin.h"
 #include "repository.h"
 #include "replace-object.h"
@@ -508,9 +508,9 @@
  * LF separated.  Its base points at a statically allocated buffer that
  * contains "/the/directory/corresponding/to/.git/objects/...", while
  * its name points just after the slash at the end of ".git/objects/"
- * in the example above, and has enough space to hold 40-byte hex
- * SHA1, an extra slash for the first level indirection, and the
- * terminating NUL.
+ * in the example above, and has enough space to hold all hex characters
+ * of the object ID, an extra slash for the first level indirection, and
+ * the terminating NUL.
  */
 static void read_info_alternates(struct repository *r,
 				 const char *relative_base,
diff --git a/sha1-name.c b/object-name.c
similarity index 98%
rename from sha1-name.c
rename to object-name.c
index 0b23b86..64202de 100644
--- a/sha1-name.c
+++ b/object-name.c
@@ -85,7 +85,7 @@
 	/* otherwise, current can be discarded and candidate is still good */
 }
 
-static int match_sha(unsigned, const unsigned char *, const unsigned char *);
+static int match_hash(unsigned, const unsigned char *, const unsigned char *);
 
 static void find_short_object_filename(struct disambiguate_state *ds)
 {
@@ -102,7 +102,7 @@
 		while (!ds->ambiguous && pos < loose_objects->nr) {
 			const struct object_id *oid;
 			oid = loose_objects->oid + pos;
-			if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash))
+			if (!match_hash(ds->len, ds->bin_pfx.hash, oid->hash))
 				break;
 			update_candidates(ds, oid);
 			pos++;
@@ -110,7 +110,7 @@
 	}
 }
 
-static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
+static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b)
 {
 	do {
 		if (*a != *b)
@@ -145,7 +145,7 @@
 	for (i = first; i < num && !ds->ambiguous; i++) {
 		struct object_id oid;
 		current = nth_midxed_object_oid(&oid, m, i);
-		if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash))
+		if (!match_hash(ds->len, ds->bin_pfx.hash, current->hash))
 			break;
 		update_candidates(ds, current);
 	}
@@ -173,7 +173,7 @@
 	for (i = first; i < num && !ds->ambiguous; i++) {
 		struct object_id oid;
 		nth_packed_object_id(&oid, p, i);
-		if (!match_sha(ds->len, ds->bin_pfx.hash, oid.hash))
+		if (!match_hash(ds->len, ds->bin_pfx.hash, oid.hash))
 			break;
 		update_candidates(ds, &oid);
 	}
@@ -483,7 +483,7 @@
 	if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
 		struct oid_array collect = OID_ARRAY_INIT;
 
-		error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
+		error(_("short object ID %s is ambiguous"), ds.hex_pfx);
 
 		/*
 		 * We may still have ambiguity if we simply saw a series of
@@ -1811,7 +1811,7 @@
 	if (!ret)
 		return ret;
 	/*
-	 * sha1:path --> object name of path in ent sha1
+	 * tree:path --> object name of path in tree
 	 * :path -> object name of absolute path in index
 	 * :./path -> object name of path relative to cwd in index
 	 * :[0-3]:path -> object name of path in index at stage
@@ -1949,6 +1949,6 @@
 					 struct object_context *oc)
 {
 	if (flags & GET_OID_FOLLOW_SYMLINKS && flags & GET_OID_ONLY_TO_DIE)
-		BUG("incompatible flags for get_sha1_with_context");
+		BUG("incompatible flags for get_oid_with_context");
 	return get_oid_with_context_1(repo, str, flags, NULL, oid, oc);
 }
diff --git a/object-store.h b/object-store.h
index c4fc9dd..541dab0 100644
--- a/object-store.h
+++ b/object-store.h
@@ -85,6 +85,9 @@
 		 multi_pack_index:1;
 	unsigned char hash[GIT_MAX_RAWSZ];
 	struct revindex_entry *revindex;
+	const uint32_t *revindex_data;
+	const uint32_t *revindex_map;
+	size_t revindex_size;
 	/* something like ".git/objects/pack/xxxxx.pack" */
 	char pack_name[FLEX_ARRAY]; /* more */
 };
diff --git a/object.c b/object.c
index 68f80b0..98017be 100644
--- a/object.c
+++ b/object.c
@@ -412,15 +412,16 @@
 }
 
 /*
- * Return true iff array already contains an entry with name.
+ * Return true if array already contains an entry.
  */
-static int contains_name(struct object_array *array, const char *name)
+static int contains_object(struct object_array *array,
+			   const struct object *item, const char *name)
 {
 	unsigned nr = array->nr, i;
 	struct object_array_entry *object = array->objects;
 
 	for (i = 0; i < nr; i++, object++)
-		if (!strcmp(object->name, name))
+		if (item == object->item && !strcmp(object->name, name))
 			return 1;
 	return 0;
 }
@@ -432,7 +433,8 @@
 
 	array->nr = 0;
 	for (src = 0; src < nr; src++) {
-		if (!contains_name(array, objects[src].name)) {
+		if (!contains_object(array, objects[src].item,
+				     objects[src].name)) {
 			if (src != array->nr)
 				objects[array->nr] = objects[src];
 			array->nr++;
diff --git a/oid-array.c b/oid-array.c
index 8e1bced..73ba76e 100644
--- a/oid-array.c
+++ b/oid-array.c
@@ -1,6 +1,6 @@
 #include "cache.h"
 #include "oid-array.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 
 void oid_array_append(struct oid_array *array, const struct object_id *oid)
 {
@@ -22,16 +22,16 @@
 	array->sorted = 1;
 }
 
-static const unsigned char *sha1_access(size_t index, void *table)
+static const struct object_id *oid_access(size_t index, const void *table)
 {
-	struct object_id *array = table;
-	return array[index].hash;
+	const struct object_id *array = table;
+	return &array[index];
 }
 
 int oid_array_lookup(struct oid_array *array, const struct object_id *oid)
 {
 	oid_array_sort(array);
-	return sha1_pos(oid->hash, array->oid, array->nr, sha1_access);
+	return oid_pos(oid, array->oid, array->nr, oid_access);
 }
 
 void oid_array_clear(struct oid_array *array)
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 5e998bd..88d9e69 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -9,9 +9,10 @@
 #include "pack-revindex.h"
 #include "pack.h"
 #include "pack-bitmap.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "pack-objects.h"
 #include "commit-reach.h"
+#include "prio-queue.h"
 
 struct bitmapped_commit {
 	struct commit *commit;
@@ -29,7 +30,6 @@
 	struct ewah_bitmap *tags;
 
 	kh_oid_map_t *bitmaps;
-	kh_oid_map_t *reused;
 	struct packing_data *to_pack;
 
 	struct bitmapped_commit *selected;
@@ -110,10 +110,8 @@
 /**
  * Compute the actual bitmaps
  */
-static struct object **seen_objects;
-static unsigned int seen_objects_nr, seen_objects_alloc;
 
-static inline void push_bitmapped_commit(struct commit *commit, struct ewah_bitmap *reused)
+static inline void push_bitmapped_commit(struct commit *commit)
 {
 	if (writer.selected_nr >= writer.selected_alloc) {
 		writer.selected_alloc = (writer.selected_alloc + 32) * 2;
@@ -121,27 +119,12 @@
 	}
 
 	writer.selected[writer.selected_nr].commit = commit;
-	writer.selected[writer.selected_nr].bitmap = reused;
+	writer.selected[writer.selected_nr].bitmap = NULL;
 	writer.selected[writer.selected_nr].flags = 0;
 
 	writer.selected_nr++;
 }
 
-static inline void mark_as_seen(struct object *object)
-{
-	ALLOC_GROW(seen_objects, seen_objects_nr + 1, seen_objects_alloc);
-	seen_objects[seen_objects_nr++] = object;
-}
-
-static inline void reset_all_seen(void)
-{
-	unsigned int i;
-	for (i = 0; i < seen_objects_nr; ++i) {
-		seen_objects[i]->flags &= ~(SEEN | ADDED | SHOWN);
-	}
-	seen_objects_nr = 0;
-}
-
 static uint32_t find_object_pos(const struct object_id *oid)
 {
 	struct object_entry *entry = packlist_find(writer.to_pack, oid);
@@ -154,60 +137,6 @@
 	return oe_in_pack_pos(writer.to_pack, entry);
 }
 
-static void show_object(struct object *object, const char *name, void *data)
-{
-	struct bitmap *base = data;
-	bitmap_set(base, find_object_pos(&object->oid));
-	mark_as_seen(object);
-}
-
-static void show_commit(struct commit *commit, void *data)
-{
-	mark_as_seen((struct object *)commit);
-}
-
-static int
-add_to_include_set(struct bitmap *base, struct commit *commit)
-{
-	khiter_t hash_pos;
-	uint32_t bitmap_pos = find_object_pos(&commit->object.oid);
-
-	if (bitmap_get(base, bitmap_pos))
-		return 0;
-
-	hash_pos = kh_get_oid_map(writer.bitmaps, commit->object.oid);
-	if (hash_pos < kh_end(writer.bitmaps)) {
-		struct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);
-		bitmap_or_ewah(base, bc->bitmap);
-		return 0;
-	}
-
-	bitmap_set(base, bitmap_pos);
-	return 1;
-}
-
-static int
-should_include(struct commit *commit, void *_data)
-{
-	struct bitmap *base = _data;
-
-	if (!add_to_include_set(base, commit)) {
-		struct commit_list *parent = commit->parents;
-
-		mark_as_seen((struct object *)commit);
-
-		while (parent) {
-			parent->item->object.flags |= SEEN;
-			mark_as_seen((struct object *)parent->item);
-			parent = parent->next;
-		}
-
-		return 0;
-	}
-
-	return 1;
-}
-
 static void compute_xor_offsets(void)
 {
 	static const int MAX_XOR_OFFSET_SEARCH = 10;
@@ -248,79 +177,326 @@
 	}
 }
 
+struct bb_commit {
+	struct commit_list *reverse_edges;
+	struct bitmap *commit_mask;
+	struct bitmap *bitmap;
+	unsigned selected:1,
+		 maximal:1;
+	unsigned idx; /* within selected array */
+};
+
+define_commit_slab(bb_data, struct bb_commit);
+
+struct bitmap_builder {
+	struct bb_data data;
+	struct commit **commits;
+	size_t commits_nr, commits_alloc;
+};
+
+static void bitmap_builder_init(struct bitmap_builder *bb,
+				struct bitmap_writer *writer,
+				struct bitmap_index *old_bitmap)
+{
+	struct rev_info revs;
+	struct commit *commit;
+	struct commit_list *reusable = NULL;
+	struct commit_list *r;
+	unsigned int i, num_maximal = 0;
+
+	memset(bb, 0, sizeof(*bb));
+	init_bb_data(&bb->data);
+
+	reset_revision_walk();
+	repo_init_revisions(writer->to_pack->repo, &revs, NULL);
+	revs.topo_order = 1;
+	revs.first_parent_only = 1;
+
+	for (i = 0; i < writer->selected_nr; i++) {
+		struct commit *c = writer->selected[i].commit;
+		struct bb_commit *ent = bb_data_at(&bb->data, c);
+
+		ent->selected = 1;
+		ent->maximal = 1;
+		ent->idx = i;
+
+		ent->commit_mask = bitmap_new();
+		bitmap_set(ent->commit_mask, i);
+
+		add_pending_object(&revs, &c->object, "");
+	}
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+
+	while ((commit = get_revision(&revs))) {
+		struct commit_list *p = commit->parents;
+		struct bb_commit *c_ent;
+
+		parse_commit_or_die(commit);
+
+		c_ent = bb_data_at(&bb->data, commit);
+
+		/*
+		 * If there is no commit_mask, there is no reason to iterate
+		 * over this commit; it is not selected (if it were, it would
+		 * not have a blank commit mask) and all its children have
+		 * existing bitmaps (see the comment starting with "This commit
+		 * has an existing bitmap" below), so it does not contribute
+		 * anything to the final bitmap file or its descendants.
+		 */
+		if (!c_ent->commit_mask)
+			continue;
+
+		if (old_bitmap && bitmap_for_commit(old_bitmap, commit)) {
+			/*
+			 * This commit has an existing bitmap, so we can
+			 * get its bits immediately without an object
+			 * walk. That is, it is reusable as-is and there is no
+			 * need to continue walking beyond it.
+			 *
+			 * Mark it as such and add it to bb->commits separately
+			 * to avoid allocating a position in the commit mask.
+			 */
+			commit_list_insert(commit, &reusable);
+			goto next;
+		}
+
+		if (c_ent->maximal) {
+			num_maximal++;
+			ALLOC_GROW(bb->commits, bb->commits_nr + 1, bb->commits_alloc);
+			bb->commits[bb->commits_nr++] = commit;
+		}
+
+		if (p) {
+			struct bb_commit *p_ent = bb_data_at(&bb->data, p->item);
+			int c_not_p, p_not_c;
+
+			if (!p_ent->commit_mask) {
+				p_ent->commit_mask = bitmap_new();
+				c_not_p = 1;
+				p_not_c = 0;
+			} else {
+				c_not_p = bitmap_is_subset(c_ent->commit_mask, p_ent->commit_mask);
+				p_not_c = bitmap_is_subset(p_ent->commit_mask, c_ent->commit_mask);
+			}
+
+			if (!c_not_p)
+				continue;
+
+			bitmap_or(p_ent->commit_mask, c_ent->commit_mask);
+
+			if (p_not_c)
+				p_ent->maximal = 1;
+			else {
+				p_ent->maximal = 0;
+				free_commit_list(p_ent->reverse_edges);
+				p_ent->reverse_edges = NULL;
+			}
+
+			if (c_ent->maximal) {
+				commit_list_insert(commit, &p_ent->reverse_edges);
+			} else {
+				struct commit_list *cc = c_ent->reverse_edges;
+
+				for (; cc; cc = cc->next) {
+					if (!commit_list_contains(cc->item, p_ent->reverse_edges))
+						commit_list_insert(cc->item, &p_ent->reverse_edges);
+				}
+			}
+		}
+
+next:
+		bitmap_free(c_ent->commit_mask);
+		c_ent->commit_mask = NULL;
+	}
+
+	for (r = reusable; r; r = r->next) {
+		ALLOC_GROW(bb->commits, bb->commits_nr + 1, bb->commits_alloc);
+		bb->commits[bb->commits_nr++] = r->item;
+	}
+
+	trace2_data_intmax("pack-bitmap-write", the_repository,
+			   "num_selected_commits", writer->selected_nr);
+	trace2_data_intmax("pack-bitmap-write", the_repository,
+			   "num_maximal_commits", num_maximal);
+
+	free_commit_list(reusable);
+}
+
+static void bitmap_builder_clear(struct bitmap_builder *bb)
+{
+	clear_bb_data(&bb->data);
+	free(bb->commits);
+	bb->commits_nr = bb->commits_alloc = 0;
+}
+
+static void fill_bitmap_tree(struct bitmap *bitmap,
+			     struct tree *tree)
+{
+	uint32_t pos;
+	struct tree_desc desc;
+	struct name_entry entry;
+
+	/*
+	 * If our bit is already set, then there is nothing to do. Both this
+	 * tree and all of its children will be set.
+	 */
+	pos = find_object_pos(&tree->object.oid);
+	if (bitmap_get(bitmap, pos))
+		return;
+	bitmap_set(bitmap, pos);
+
+	if (parse_tree(tree) < 0)
+		die("unable to load tree object %s",
+		    oid_to_hex(&tree->object.oid));
+	init_tree_desc(&desc, tree->buffer, tree->size);
+
+	while (tree_entry(&desc, &entry)) {
+		switch (object_type(entry.mode)) {
+		case OBJ_TREE:
+			fill_bitmap_tree(bitmap,
+					 lookup_tree(the_repository, &entry.oid));
+			break;
+		case OBJ_BLOB:
+			bitmap_set(bitmap, find_object_pos(&entry.oid));
+			break;
+		default:
+			/* Gitlink, etc; not reachable */
+			break;
+		}
+	}
+
+	free_tree_buffer(tree);
+}
+
+static void fill_bitmap_commit(struct bb_commit *ent,
+			       struct commit *commit,
+			       struct prio_queue *queue,
+			       struct prio_queue *tree_queue,
+			       struct bitmap_index *old_bitmap,
+			       const uint32_t *mapping)
+{
+	if (!ent->bitmap)
+		ent->bitmap = bitmap_new();
+
+	prio_queue_put(queue, commit);
+
+	while (queue->nr) {
+		struct commit_list *p;
+		struct commit *c = prio_queue_get(queue);
+
+		if (old_bitmap && mapping) {
+			struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+			/*
+			 * If this commit has an old bitmap, then translate that
+			 * bitmap and add its bits to this one. No need to walk
+			 * parents or the tree for this commit.
+			 */
+			if (old && !rebuild_bitmap(mapping, old, ent->bitmap))
+				continue;
+		}
+
+		/*
+		 * Mark ourselves and queue our tree. The commit
+		 * walk ensures we cover all parents.
+		 */
+		bitmap_set(ent->bitmap, find_object_pos(&c->object.oid));
+		prio_queue_put(tree_queue, get_commit_tree(c));
+
+		for (p = c->parents; p; p = p->next) {
+			int pos = find_object_pos(&p->item->object.oid);
+			if (!bitmap_get(ent->bitmap, pos)) {
+				bitmap_set(ent->bitmap, pos);
+				prio_queue_put(queue, p->item);
+			}
+		}
+	}
+
+	while (tree_queue->nr)
+		fill_bitmap_tree(ent->bitmap, prio_queue_get(tree_queue));
+}
+
+static void store_selected(struct bb_commit *ent, struct commit *commit)
+{
+	struct bitmapped_commit *stored = &writer.selected[ent->idx];
+	khiter_t hash_pos;
+	int hash_ret;
+
+	stored->bitmap = bitmap_to_ewah(ent->bitmap);
+
+	hash_pos = kh_put_oid_map(writer.bitmaps, commit->object.oid, &hash_ret);
+	if (hash_ret == 0)
+		die("Duplicate entry when writing index: %s",
+		    oid_to_hex(&commit->object.oid));
+	kh_value(writer.bitmaps, hash_pos) = stored;
+}
+
 void bitmap_writer_build(struct packing_data *to_pack)
 {
-	static const double REUSE_BITMAP_THRESHOLD = 0.2;
-
-	int i, reuse_after, need_reset;
-	struct bitmap *base = bitmap_new();
-	struct rev_info revs;
+	struct bitmap_builder bb;
+	size_t i;
+	int nr_stored = 0; /* for progress */
+	struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+	struct prio_queue tree_queue = { NULL };
+	struct bitmap_index *old_bitmap;
+	uint32_t *mapping;
 
 	writer.bitmaps = kh_init_oid_map();
 	writer.to_pack = to_pack;
 
 	if (writer.show_progress)
 		writer.progress = start_progress("Building bitmaps", writer.selected_nr);
+	trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
+			    the_repository);
 
-	repo_init_revisions(to_pack->repo, &revs, NULL);
-	revs.tag_objects = 1;
-	revs.tree_objects = 1;
-	revs.blob_objects = 1;
-	revs.no_walk = 0;
+	old_bitmap = prepare_bitmap_git(to_pack->repo);
+	if (old_bitmap)
+		mapping = create_bitmap_mapping(old_bitmap, to_pack);
+	else
+		mapping = NULL;
 
-	revs.include_check = should_include;
-	reset_revision_walk();
+	bitmap_builder_init(&bb, &writer, old_bitmap);
+	for (i = bb.commits_nr; i > 0; i--) {
+		struct commit *commit = bb.commits[i-1];
+		struct bb_commit *ent = bb_data_at(&bb.data, commit);
+		struct commit *child;
+		int reused = 0;
 
-	reuse_after = writer.selected_nr * REUSE_BITMAP_THRESHOLD;
-	need_reset = 0;
+		fill_bitmap_commit(ent, commit, &queue, &tree_queue,
+				   old_bitmap, mapping);
 
-	for (i = writer.selected_nr - 1; i >= 0; --i) {
-		struct bitmapped_commit *stored;
-		struct object *object;
+		if (ent->selected) {
+			store_selected(ent, commit);
+			nr_stored++;
+			display_progress(writer.progress, nr_stored);
+		}
 
-		khiter_t hash_pos;
-		int hash_ret;
+		while ((child = pop_commit(&ent->reverse_edges))) {
+			struct bb_commit *child_ent =
+				bb_data_at(&bb.data, child);
 
-		stored = &writer.selected[i];
-		object = (struct object *)stored->commit;
-
-		if (stored->bitmap == NULL) {
-			if (i < writer.selected_nr - 1 &&
-			    (need_reset ||
-			     !in_merge_bases(writer.selected[i + 1].commit,
-					     stored->commit))) {
-			    bitmap_reset(base);
-			    reset_all_seen();
+			if (child_ent->bitmap)
+				bitmap_or(child_ent->bitmap, ent->bitmap);
+			else if (reused)
+				child_ent->bitmap = bitmap_dup(ent->bitmap);
+			else {
+				child_ent->bitmap = ent->bitmap;
+				reused = 1;
 			}
-
-			add_pending_object(&revs, object, "");
-			revs.include_check_data = base;
-
-			if (prepare_revision_walk(&revs))
-				die("revision walk setup failed");
-
-			traverse_commit_list(&revs, show_commit, show_object, base);
-
-			object_array_clear(&revs.pending);
-
-			stored->bitmap = bitmap_to_ewah(base);
-			need_reset = 0;
-		} else
-			need_reset = 1;
-
-		if (i >= reuse_after)
-			stored->flags |= BITMAP_FLAG_REUSE;
-
-		hash_pos = kh_put_oid_map(writer.bitmaps, object->oid, &hash_ret);
-		if (hash_ret == 0)
-			die("Duplicate entry when writing index: %s",
-			    oid_to_hex(&object->oid));
-
-		kh_value(writer.bitmaps, hash_pos) = stored;
-		display_progress(writer.progress, writer.selected_nr - i);
+		}
+		if (!reused)
+			bitmap_free(ent->bitmap);
+		ent->bitmap = NULL;
 	}
+	clear_prio_queue(&queue);
+	clear_prio_queue(&tree_queue);
+	bitmap_builder_clear(&bb);
+	free(mapping);
 
-	bitmap_free(base);
+	trace2_region_leave("pack-bitmap-write", "building_bitmaps_total",
+			    the_repository);
+
 	stop_progress(&writer.progress);
 
 	compute_xor_offsets();
@@ -360,35 +536,6 @@
 	return (long)b->date - (long)a->date;
 }
 
-void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)
-{
-	struct bitmap_index *bitmap_git;
-	if (!(bitmap_git = prepare_bitmap_git(to_pack->repo)))
-		return;
-
-	writer.reused = kh_init_oid_map();
-	rebuild_existing_bitmaps(bitmap_git, to_pack, writer.reused,
-				 writer.show_progress);
-	/*
-	 * NEEDSWORK: rebuild_existing_bitmaps() makes writer.reused reference
-	 * some bitmaps in bitmap_git, so we can't free the latter.
-	 */
-}
-
-static struct ewah_bitmap *find_reused_bitmap(const struct object_id *oid)
-{
-	khiter_t hash_pos;
-
-	if (!writer.reused)
-		return NULL;
-
-	hash_pos = kh_get_oid_map(writer.reused, *oid);
-	if (hash_pos >= kh_end(writer.reused))
-		return NULL;
-
-	return kh_value(writer.reused, hash_pos);
-}
-
 void bitmap_writer_select_commits(struct commit **indexed_commits,
 				  unsigned int indexed_commits_nr,
 				  int max_bitmaps)
@@ -402,12 +549,11 @@
 
 	if (indexed_commits_nr < 100) {
 		for (i = 0; i < indexed_commits_nr; ++i)
-			push_bitmapped_commit(indexed_commits[i], NULL);
+			push_bitmapped_commit(indexed_commits[i]);
 		return;
 	}
 
 	for (;;) {
-		struct ewah_bitmap *reused_bitmap = NULL;
 		struct commit *chosen = NULL;
 
 		next = next_commit_index(i);
@@ -422,15 +568,13 @@
 
 		if (next == 0) {
 			chosen = indexed_commits[i];
-			reused_bitmap = find_reused_bitmap(&chosen->object.oid);
 		} else {
 			chosen = indexed_commits[i + next];
 
 			for (j = 0; j <= next; ++j) {
 				struct commit *cm = indexed_commits[i + j];
 
-				reused_bitmap = find_reused_bitmap(&cm->object.oid);
-				if (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {
+				if ((cm->object.flags & NEEDS_BITMAP) != 0) {
 					chosen = cm;
 					break;
 				}
@@ -440,7 +584,7 @@
 			}
 		}
 
-		push_bitmapped_commit(chosen, reused_bitmap);
+		push_bitmapped_commit(chosen);
 
 		i += next + 1;
 		display_progress(writer.progress, i);
@@ -466,10 +610,10 @@
 		die("Failed to write bitmap index");
 }
 
-static const unsigned char *sha1_access(size_t pos, void *table)
+static const struct object_id *oid_access(size_t pos, const void *table)
 {
-	struct pack_idx_entry **index = table;
-	return index[pos]->oid.hash;
+	const struct pack_idx_entry * const *index = table;
+	return &index[pos]->oid;
 }
 
 static void write_selected_commits_v1(struct hashfile *f,
@@ -482,7 +626,7 @@
 		struct bitmapped_commit *stored = &writer.selected[i];
 
 		int commit_pos =
-			sha1_pos(stored->commit->object.oid.hash, index, index_nr, sha1_access);
+			oid_pos(&stored->commit->object.oid, index, index_nr, oid_access);
 
 		if (commit_pos < 0)
 			BUG("trying to write commit not in index");
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 4077e73..1f69b5f 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -138,9 +138,10 @@
 static int load_bitmap_header(struct bitmap_index *index)
 {
 	struct bitmap_disk_header *header = (void *)index->map;
+	size_t header_size = sizeof(*header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz;
 
-	if (index->map_size < sizeof(*header) + the_hash_algo->rawsz)
-		return error("Corrupted bitmap index (missing header data)");
+	if (index->map_size < header_size + the_hash_algo->rawsz)
+		return error("Corrupted bitmap index (too small)");
 
 	if (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)
 		return error("Corrupted bitmap index file (wrong header)");
@@ -152,19 +153,23 @@
 	/* Parse known bitmap format options */
 	{
 		uint32_t flags = ntohs(header->options);
+		size_t cache_size = st_mult(index->pack->num_objects, sizeof(uint32_t));
+		unsigned char *index_end = index->map + index->map_size - the_hash_algo->rawsz;
 
 		if ((flags & BITMAP_OPT_FULL_DAG) == 0)
 			return error("Unsupported options for bitmap index file "
 				"(Git requires BITMAP_OPT_FULL_DAG)");
 
 		if (flags & BITMAP_OPT_HASH_CACHE) {
-			unsigned char *end = index->map + index->map_size - the_hash_algo->rawsz;
-			index->hashes = ((uint32_t *)end) - index->pack->num_objects;
+			if (cache_size > index_end - index->map - header_size)
+				return error("corrupted bitmap index file (too short to fit hash cache)");
+			index->hashes = (void *)(index_end - cache_size);
+			index_end -= cache_size;
 		}
 	}
 
 	index->entry_count = ntohl(header->entry_count);
-	index->map_pos += sizeof(*header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz;
+	index->map_pos += header_size;
 	return 0;
 }
 
@@ -224,11 +229,16 @@
 		uint32_t commit_idx_pos;
 		struct object_id oid;
 
+		if (index->map_size - index->map_pos < 6)
+			return error("corrupt ewah bitmap: truncated header for entry %d", i);
+
 		commit_idx_pos = read_be32(index->map, &index->map_pos);
 		xor_offset = read_u8(index->map, &index->map_pos);
 		flags = read_u8(index->map, &index->map_pos);
 
-		nth_packed_object_id(&oid, index->pack, commit_idx_pos);
+		if (nth_packed_object_id(&oid, index->pack, commit_idx_pos) < 0)
+			return error("corrupt ewah bitmap: commit index %u out of range",
+				     (unsigned)commit_idx_pos);
 
 		bitmap = read_bitmap_1(index);
 		if (!bitmap)
@@ -370,6 +380,16 @@
 	struct bitmap *seen;
 };
 
+struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
+				      struct commit *commit)
+{
+	khiter_t hash_pos = kh_get_oid_map(bitmap_git->bitmaps,
+					   commit->object.oid);
+	if (hash_pos >= kh_end(bitmap_git->bitmaps))
+		return NULL;
+	return lookup_stored_bitmap(kh_value(bitmap_git->bitmaps, hash_pos));
+}
+
 static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
 					   const struct object_id *oid)
 {
@@ -387,11 +407,14 @@
 static inline int bitmap_position_packfile(struct bitmap_index *bitmap_git,
 					   const struct object_id *oid)
 {
+	uint32_t pos;
 	off_t offset = find_pack_entry_one(oid->hash, bitmap_git->pack);
 	if (!offset)
 		return -1;
 
-	return find_revindex_position(bitmap_git->pack, offset);
+	if (offset_to_pack_pos(bitmap_git->pack, offset, &pos) < 0)
+		return -1;
+	return pos;
 }
 
 static int bitmap_position(struct bitmap_index *bitmap_git,
@@ -455,10 +478,10 @@
 
 static int add_to_include_set(struct bitmap_index *bitmap_git,
 			      struct include_data *data,
-			      const struct object_id *oid,
+			      struct commit *commit,
 			      int bitmap_pos)
 {
-	khiter_t hash_pos;
+	struct ewah_bitmap *partial;
 
 	if (data->seen && bitmap_get(data->seen, bitmap_pos))
 		return 0;
@@ -466,10 +489,9 @@
 	if (bitmap_get(data->base, bitmap_pos))
 		return 0;
 
-	hash_pos = kh_get_oid_map(bitmap_git->bitmaps, *oid);
-	if (hash_pos < kh_end(bitmap_git->bitmaps)) {
-		struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, hash_pos);
-		bitmap_or_ewah(data->base, lookup_stored_bitmap(st));
+	partial = bitmap_for_commit(bitmap_git, commit);
+	if (partial) {
+		bitmap_or_ewah(data->base, partial);
 		return 0;
 	}
 
@@ -488,8 +510,7 @@
 						  (struct object *)commit,
 						  NULL);
 
-	if (!add_to_include_set(data->bitmap_git, data, &commit->object.oid,
-				bitmap_pos)) {
+	if (!add_to_include_set(data->bitmap_git, data, commit, bitmap_pos)) {
 		struct commit_list *parent = commit->parents;
 
 		while (parent) {
@@ -503,6 +524,23 @@
 	return 1;
 }
 
+static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
+				struct bitmap **base,
+				struct commit *commit)
+{
+	struct ewah_bitmap *or_with = bitmap_for_commit(bitmap_git, commit);
+
+	if (!or_with)
+		return 0;
+
+	if (*base == NULL)
+		*base = ewah_to_bitmap(or_with);
+	else
+		bitmap_or_ewah(*base, or_with);
+
+	return 1;
+}
+
 static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
 				   struct rev_info *revs,
 				   struct object_list *roots,
@@ -526,21 +564,10 @@
 		struct object *object = roots->item;
 		roots = roots->next;
 
-		if (object->type == OBJ_COMMIT) {
-			khiter_t pos = kh_get_oid_map(bitmap_git->bitmaps, object->oid);
-
-			if (pos < kh_end(bitmap_git->bitmaps)) {
-				struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
-				struct ewah_bitmap *or_with = lookup_stored_bitmap(st);
-
-				if (base == NULL)
-					base = ewah_to_bitmap(or_with);
-				else
-					bitmap_or_ewah(base, or_with);
-
-				object->flags |= SEEN;
-				continue;
-			}
+		if (object->type == OBJ_COMMIT &&
+		    add_commit_to_bitmap(bitmap_git, &base, (struct commit *)object)) {
+			object->flags |= SEEN;
+			continue;
 		}
 
 		object_list_insert(object, &not_mapped);
@@ -684,21 +711,22 @@
 
 		for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
 			struct object_id oid;
-			struct revindex_entry *entry;
-			uint32_t hash = 0;
+			uint32_t hash = 0, index_pos;
+			off_t ofs;
 
 			if ((word >> offset) == 0)
 				break;
 
 			offset += ewah_bit_ctz64(word >> offset);
 
-			entry = &bitmap_git->pack->revindex[pos + offset];
-			nth_packed_object_id(&oid, bitmap_git->pack, entry->nr);
+			index_pos = pack_pos_to_index(bitmap_git->pack, pos + offset);
+			ofs = pack_pos_to_offset(bitmap_git->pack, pos + offset);
+			nth_packed_object_id(&oid, bitmap_git->pack, index_pos);
 
 			if (bitmap_git->hashes)
-				hash = get_be32(bitmap_git->hashes + entry->nr);
+				hash = get_be32(bitmap_git->hashes + index_pos);
 
-			show_reach(&oid, object_type, 0, hash, bitmap_git->pack, entry->offset);
+			show_reach(&oid, object_type, 0, hash, bitmap_git->pack, ofs);
 		}
 	}
 }
@@ -807,11 +835,11 @@
 	oi.sizep = &size;
 
 	if (pos < pack->num_objects) {
-		struct revindex_entry *entry = &pack->revindex[pos];
-		if (packed_object_info(the_repository, pack,
-				       entry->offset, &oi) < 0) {
+		off_t ofs = pack_pos_to_offset(pack, pos);
+		if (packed_object_info(the_repository, pack, ofs, &oi) < 0) {
 			struct object_id oid;
-			nth_packed_object_id(&oid, pack, entry->nr);
+			nth_packed_object_id(&oid, pack,
+					     pack_pos_to_index(pack, pos));
 			die(_("unable to get size of %s"), oid_to_hex(&oid));
 		}
 	} else {
@@ -1041,23 +1069,21 @@
 			      struct bitmap *reuse,
 			      struct pack_window **w_curs)
 {
-	struct revindex_entry *revidx;
-	off_t offset;
+	off_t offset, header;
 	enum object_type type;
 	unsigned long size;
 
 	if (pos >= bitmap_git->pack->num_objects)
 		return; /* not actually in the pack */
 
-	revidx = &bitmap_git->pack->revindex[pos];
-	offset = revidx->offset;
+	offset = header = pack_pos_to_offset(bitmap_git->pack, pos);
 	type = unpack_object_header(bitmap_git->pack, w_curs, &offset, &size);
 	if (type < 0)
 		return; /* broken packfile, punt */
 
 	if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) {
 		off_t base_offset;
-		int base_pos;
+		uint32_t base_pos;
 
 		/*
 		 * Find the position of the base object so we can look it up
@@ -1068,11 +1094,10 @@
 		 * more detail.
 		 */
 		base_offset = get_delta_base(bitmap_git->pack, w_curs,
-					     &offset, type, revidx->offset);
+					     &offset, type, header);
 		if (!base_offset)
 			return;
-		base_pos = find_revindex_position(bitmap_git->pack, base_offset);
-		if (base_pos < 0)
+		if (offset_to_pack_pos(bitmap_git->pack, base_offset, &base_pos) < 0)
 			return;
 
 		/*
@@ -1272,10 +1297,10 @@
 {
 	struct object *root;
 	struct bitmap *result = NULL;
-	khiter_t pos;
 	size_t result_popcnt;
 	struct bitmap_test_data tdata;
 	struct bitmap_index *bitmap_git;
+	struct ewah_bitmap *bm;
 
 	if (!(bitmap_git = prepare_bitmap_git(revs->repo)))
 		die("failed to load bitmap indexes");
@@ -1287,12 +1312,9 @@
 		bitmap_git->version, bitmap_git->entry_count);
 
 	root = revs->pending.objects[0].item;
-	pos = kh_get_oid_map(bitmap_git->bitmaps, root->oid);
+	bm = bitmap_for_commit(bitmap_git, (struct commit *)root);
 
-	if (pos < kh_end(bitmap_git->bitmaps)) {
-		struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
-		struct ewah_bitmap *bm = lookup_stored_bitmap(st);
-
+	if (bm) {
 		fprintf(stderr, "Found bitmap for %s. %d bits / %08x checksum\n",
 			oid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));
 
@@ -1323,14 +1345,14 @@
 	if (bitmap_equals(result, tdata.base))
 		fprintf(stderr, "OK!\n");
 	else
-		fprintf(stderr, "Mismatch!\n");
+		die("mismatch in bitmap results");
 
 	free_bitmap_index(bitmap_git);
 }
 
-static int rebuild_bitmap(uint32_t *reposition,
-			  struct ewah_bitmap *source,
-			  struct bitmap *dest)
+int rebuild_bitmap(const uint32_t *reposition,
+		   struct ewah_bitmap *source,
+		   struct bitmap *dest)
 {
 	uint32_t pos = 0;
 	struct ewah_iterator it;
@@ -1359,63 +1381,28 @@
 	return 0;
 }
 
-int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git,
-			     struct packing_data *mapping,
-			     kh_oid_map_t *reused_bitmaps,
-			     int show_progress)
+uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
+				struct packing_data *mapping)
 {
 	uint32_t i, num_objects;
 	uint32_t *reposition;
-	struct bitmap *rebuild;
-	struct stored_bitmap *stored;
-	struct progress *progress = NULL;
-
-	khiter_t hash_pos;
-	int hash_ret;
 
 	num_objects = bitmap_git->pack->num_objects;
 	reposition = xcalloc(num_objects, sizeof(uint32_t));
 
 	for (i = 0; i < num_objects; ++i) {
 		struct object_id oid;
-		struct revindex_entry *entry;
 		struct object_entry *oe;
 
-		entry = &bitmap_git->pack->revindex[i];
-		nth_packed_object_id(&oid, bitmap_git->pack, entry->nr);
+		nth_packed_object_id(&oid, bitmap_git->pack,
+				     pack_pos_to_index(bitmap_git->pack, i));
 		oe = packlist_find(mapping, &oid);
 
 		if (oe)
 			reposition[i] = oe_in_pack_pos(mapping, oe) + 1;
 	}
 
-	rebuild = bitmap_new();
-	i = 0;
-
-	if (show_progress)
-		progress = start_progress("Reusing bitmaps", 0);
-
-	kh_foreach_value(bitmap_git->bitmaps, stored, {
-		if (stored->flags & BITMAP_FLAG_REUSE) {
-			if (!rebuild_bitmap(reposition,
-					    lookup_stored_bitmap(stored),
-					    rebuild)) {
-				hash_pos = kh_put_oid_map(reused_bitmaps,
-							  stored->oid,
-							  &hash_ret);
-				kh_value(reused_bitmaps, hash_pos) =
-					bitmap_to_ewah(rebuild);
-			}
-			bitmap_reset(rebuild);
-			display_progress(progress, ++i);
-		}
-	});
-
-	stop_progress(&progress);
-
-	free(reposition);
-	bitmap_free(rebuild);
-	return 0;
+	return reposition;
 }
 
 void free_bitmap_index(struct bitmap_index *b)
@@ -1443,3 +1430,84 @@
 	return bitmap_git &&
 		bitmap_walk_contains(bitmap_git, bitmap_git->haves, oid);
 }
+
+static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
+				     enum object_type object_type)
+{
+	struct bitmap *result = bitmap_git->result;
+	struct packed_git *pack = bitmap_git->pack;
+	off_t total = 0;
+	struct ewah_iterator it;
+	eword_t filter;
+	size_t i;
+
+	init_type_iterator(&it, bitmap_git, object_type);
+	for (i = 0; i < result->word_alloc &&
+			ewah_iterator_next(&filter, &it); i++) {
+		eword_t word = result->words[i] & filter;
+		size_t base = (i * BITS_IN_EWORD);
+		unsigned offset;
+
+		if (!word)
+			continue;
+
+		for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+			size_t pos;
+
+			if ((word >> offset) == 0)
+				break;
+
+			offset += ewah_bit_ctz64(word >> offset);
+			pos = base + offset;
+			total += pack_pos_to_offset(pack, pos + 1) -
+				 pack_pos_to_offset(pack, pos);
+		}
+	}
+
+	return total;
+}
+
+static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)
+{
+	struct bitmap *result = bitmap_git->result;
+	struct packed_git *pack = bitmap_git->pack;
+	struct eindex *eindex = &bitmap_git->ext_index;
+	off_t total = 0;
+	struct object_info oi = OBJECT_INFO_INIT;
+	off_t object_size;
+	size_t i;
+
+	oi.disk_sizep = &object_size;
+
+	for (i = 0; i < eindex->count; i++) {
+		struct object *obj = eindex->objects[i];
+
+		if (!bitmap_get(result, pack->num_objects + i))
+			continue;
+
+		if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+			die(_("unable to get disk usage of %s"),
+			    oid_to_hex(&obj->oid));
+
+		total += object_size;
+	}
+	return total;
+}
+
+off_t get_disk_usage_from_bitmap(struct bitmap_index *bitmap_git,
+				 struct rev_info *revs)
+{
+	off_t total = 0;
+
+	total += get_disk_usage_for_type(bitmap_git, OBJ_COMMIT);
+	if (revs->tree_objects)
+		total += get_disk_usage_for_type(bitmap_git, OBJ_TREE);
+	if (revs->blob_objects)
+		total += get_disk_usage_for_type(bitmap_git, OBJ_BLOB);
+	if (revs->tag_objects)
+		total += get_disk_usage_for_type(bitmap_git, OBJ_TAG);
+
+	total += get_disk_usage_for_extended(bitmap_git);
+
+	return total;
+}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index 1203120..36d9993 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -68,12 +68,20 @@
  */
 int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_id *oid);
 
+off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
+
 void bitmap_writer_show_progress(int show);
 void bitmap_writer_set_checksum(unsigned char *sha1);
 void bitmap_writer_build_type_index(struct packing_data *to_pack,
 				    struct pack_idx_entry **index,
 				    uint32_t index_nr);
-void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack);
+uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
+				struct packing_data *mapping);
+int rebuild_bitmap(const uint32_t *reposition,
+		   struct ewah_bitmap *source,
+		   struct bitmap *dest);
+struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
+				      struct commit *commit);
 void bitmap_writer_select_commits(struct commit **indexed_commits,
 		unsigned int indexed_commits_nr, int max_bitmaps);
 void bitmap_writer_build(struct packing_data *to_pack);
diff --git a/pack-revindex.c b/pack-revindex.c
index ecdde39..83fe4de 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -2,6 +2,12 @@
 #include "pack-revindex.h"
 #include "object-store.h"
 #include "packfile.h"
+#include "config.h"
+
+struct revindex_entry {
+	off_t offset;
+	unsigned int nr;
+};
 
 /*
  * Pack index for existing packs give us easy access to the offsets into
@@ -159,27 +165,151 @@
 	sort_revindex(p->revindex, num_ent, p->pack_size);
 }
 
-int load_pack_revindex(struct packed_git *p)
+static int create_pack_revindex_in_memory(struct packed_git *p)
 {
-	if (!p->revindex) {
-		if (open_pack_index(p))
-			return -1;
-		create_pack_revindex(p);
-	}
+	if (git_env_bool(GIT_TEST_REV_INDEX_DIE_IN_MEMORY, 0))
+		die("dying as requested by '%s'",
+		    GIT_TEST_REV_INDEX_DIE_IN_MEMORY);
+	if (open_pack_index(p))
+		return -1;
+	create_pack_revindex(p);
 	return 0;
 }
 
-int find_revindex_position(struct packed_git *p, off_t ofs)
+static char *pack_revindex_filename(struct packed_git *p)
 {
-	int lo = 0;
-	int hi = p->num_objects + 1;
-	const struct revindex_entry *revindex = p->revindex;
+	size_t len;
+	if (!strip_suffix(p->pack_name, ".pack", &len))
+		BUG("pack_name does not end in .pack");
+	return xstrfmt("%.*s.rev", (int)len, p->pack_name);
+}
+
+#define RIDX_HEADER_SIZE (12)
+#define RIDX_MIN_SIZE (RIDX_HEADER_SIZE + (2 * the_hash_algo->rawsz))
+
+struct revindex_header {
+	uint32_t signature;
+	uint32_t version;
+	uint32_t hash_id;
+};
+
+static int load_revindex_from_disk(char *revindex_name,
+				   uint32_t num_objects,
+				   const uint32_t **data_p, size_t *len_p)
+{
+	int fd, ret = 0;
+	struct stat st;
+	void *data = NULL;
+	size_t revindex_size;
+	struct revindex_header *hdr;
+
+	fd = git_open(revindex_name);
+
+	if (fd < 0) {
+		ret = -1;
+		goto cleanup;
+	}
+	if (fstat(fd, &st)) {
+		ret = error_errno(_("failed to read %s"), revindex_name);
+		goto cleanup;
+	}
+
+	revindex_size = xsize_t(st.st_size);
+
+	if (revindex_size < RIDX_MIN_SIZE) {
+		ret = error(_("reverse-index file %s is too small"), revindex_name);
+		goto cleanup;
+	}
+
+	if (revindex_size - RIDX_MIN_SIZE != st_mult(sizeof(uint32_t), num_objects)) {
+		ret = error(_("reverse-index file %s is corrupt"), revindex_name);
+		goto cleanup;
+	}
+
+	data = xmmap(NULL, revindex_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	hdr = data;
+
+	if (ntohl(hdr->signature) != RIDX_SIGNATURE) {
+		ret = error(_("reverse-index file %s has unknown signature"), revindex_name);
+		goto cleanup;
+	}
+	if (ntohl(hdr->version) != 1) {
+		ret = error(_("reverse-index file %s has unsupported version %"PRIu32),
+			    revindex_name, ntohl(hdr->version));
+		goto cleanup;
+	}
+	if (!(ntohl(hdr->hash_id) == 1 || ntohl(hdr->hash_id) == 2)) {
+		ret = error(_("reverse-index file %s has unsupported hash id %"PRIu32),
+			    revindex_name, ntohl(hdr->hash_id));
+		goto cleanup;
+	}
+
+cleanup:
+	if (ret) {
+		if (data)
+			munmap(data, revindex_size);
+	} else {
+		*len_p = revindex_size;
+		*data_p = (const uint32_t *)data;
+	}
+
+	close(fd);
+	return ret;
+}
+
+static int load_pack_revindex_from_disk(struct packed_git *p)
+{
+	char *revindex_name;
+	int ret;
+	if (open_pack_index(p))
+		return -1;
+
+	revindex_name = pack_revindex_filename(p);
+
+	ret = load_revindex_from_disk(revindex_name,
+				      p->num_objects,
+				      &p->revindex_map,
+				      &p->revindex_size);
+	if (ret)
+		goto cleanup;
+
+	p->revindex_data = (const uint32_t *)((const char *)p->revindex_map + RIDX_HEADER_SIZE);
+
+cleanup:
+	free(revindex_name);
+	return ret;
+}
+
+int load_pack_revindex(struct packed_git *p)
+{
+	if (p->revindex || p->revindex_data)
+		return 0;
+
+	if (!load_pack_revindex_from_disk(p))
+		return 0;
+	else if (!create_pack_revindex_in_memory(p))
+		return 0;
+	return -1;
+}
+
+int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
+{
+	unsigned lo, hi;
+
+	if (load_pack_revindex(p) < 0)
+		return -1;
+
+	lo = 0;
+	hi = p->num_objects + 1;
 
 	do {
 		const unsigned mi = lo + (hi - lo) / 2;
-		if (revindex[mi].offset == ofs) {
-			return mi;
-		} else if (ofs < revindex[mi].offset)
+		off_t got = pack_pos_to_offset(p, mi);
+
+		if (got == ofs) {
+			*pos = mi;
+			return 0;
+		} else if (ofs < got)
 			hi = mi;
 		else
 			lo = mi + 1;
@@ -189,17 +319,30 @@
 	return -1;
 }
 
-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
+uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos)
 {
-	int pos;
+	if (!(p->revindex || p->revindex_data))
+		BUG("pack_pos_to_index: reverse index not yet loaded");
+	if (p->num_objects <= pos)
+		BUG("pack_pos_to_index: out-of-bounds object at %"PRIu32, pos);
 
-	if (load_pack_revindex(p))
-		return NULL;
+	if (p->revindex)
+		return p->revindex[pos].nr;
+	else
+		return get_be32(p->revindex_data + pos);
+}
 
-	pos = find_revindex_position(p, ofs);
+off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos)
+{
+	if (!(p->revindex || p->revindex_data))
+		BUG("pack_pos_to_index: reverse index not yet loaded");
+	if (p->num_objects < pos)
+		BUG("pack_pos_to_offset: out-of-bounds object at %"PRIu32, pos);
 
-	if (pos < 0)
-		return NULL;
-
-	return p->revindex + pos;
+	if (p->revindex)
+		return p->revindex[pos].offset;
+	else if (pos == p->num_objects)
+		return p->pack_size - the_hash_algo->rawsz;
+	else
+		return nth_packed_object_offset(p, pack_pos_to_index(p, pos));
 }
diff --git a/pack-revindex.h b/pack-revindex.h
index 848331d..ba7c82c 100644
--- a/pack-revindex.h
+++ b/pack-revindex.h
@@ -1,16 +1,74 @@
 #ifndef PACK_REVINDEX_H
 #define PACK_REVINDEX_H
 
+/**
+ * A revindex allows converting efficiently between three properties
+ * of an object within a pack:
+ *
+ * - index position: the numeric position within the list of sorted object ids
+ *   found in the .idx file
+ *
+ * - pack position: the numeric position within the list of objects in their
+ *   order within the actual .pack file (i.e., 0 is the first object in the
+ *   .pack, 1 is the second, and so on)
+ *
+ * - offset: the byte offset within the .pack file at which the object contents
+ *   can be found
+ */
+
+
+#define RIDX_SIGNATURE 0x52494458 /* "RIDX" */
+#define RIDX_VERSION 1
+
+#define GIT_TEST_WRITE_REV_INDEX "GIT_TEST_WRITE_REV_INDEX"
+#define GIT_TEST_REV_INDEX_DIE_IN_MEMORY "GIT_TEST_REV_INDEX_DIE_IN_MEMORY"
+
 struct packed_git;
 
-struct revindex_entry {
-	off_t offset;
-	unsigned int nr;
-};
-
+/*
+ * load_pack_revindex populates the revindex's internal data-structures for the
+ * given pack, returning zero on success and a negative value otherwise.
+ *
+ * If a '.rev' file is present it is mmap'd, and pointers are assigned into it
+ * (instead of using the in-memory variant).
+ */
 int load_pack_revindex(struct packed_git *p);
-int find_revindex_position(struct packed_git *p, off_t ofs);
 
-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
+/*
+ * offset_to_pack_pos converts an object offset to a pack position. This
+ * function returns zero on success, and a negative number otherwise. The
+ * parameter 'pos' is usable only on success.
+ *
+ * If the reverse index has not yet been loaded, this function loads it lazily,
+ * and returns an negative number if an error was encountered.
+ *
+ * This function runs in time O(log N) with the number of objects in the pack.
+ */
+int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos);
+
+/*
+ * pack_pos_to_index converts the given pack-relative position 'pos' by
+ * returning an index-relative position.
+ *
+ * If the reverse index has not yet been loaded, or the position is out of
+ * bounds, this function aborts.
+ *
+ * This function runs in constant time.
+ */
+uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos);
+
+/*
+ * pack_pos_to_offset converts the given pack-relative position 'pos' into a
+ * pack offset. For a pack with 'N' objects, asking for position 'N' will return
+ * the total size (in bytes) of the pack.
+ *
+ * If the reverse index has not yet been loaded, or the position is out of
+ * bounds, this function aborts.
+ *
+ * This function runs in constant time under both in-memory and on-disk reverse
+ * indexes, but an additional step is taken to consult the corresponding .idx
+ * file when using the on-disk format.
+ */
+off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);
 
 #endif
diff --git a/pack-write.c b/pack-write.c
index 3513665..2ca85a9 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "pack.h"
 #include "csum-file.h"
+#include "remote.h"
 
 void reset_pack_idx_option(struct pack_idx_option *opts)
 {
@@ -166,6 +167,113 @@
 	return index_name;
 }
 
+static int pack_order_cmp(const void *va, const void *vb, void *ctx)
+{
+	struct pack_idx_entry **objects = ctx;
+
+	off_t oa = objects[*(uint32_t*)va]->offset;
+	off_t ob = objects[*(uint32_t*)vb]->offset;
+
+	if (oa < ob)
+		return -1;
+	if (oa > ob)
+		return 1;
+	return 0;
+}
+
+static void write_rev_header(struct hashfile *f)
+{
+	uint32_t oid_version;
+	switch (hash_algo_by_ptr(the_hash_algo)) {
+	case GIT_HASH_SHA1:
+		oid_version = 1;
+		break;
+	case GIT_HASH_SHA256:
+		oid_version = 2;
+		break;
+	default:
+		die("write_rev_header: unknown hash version");
+	}
+
+	hashwrite_be32(f, RIDX_SIGNATURE);
+	hashwrite_be32(f, RIDX_VERSION);
+	hashwrite_be32(f, oid_version);
+}
+
+static void write_rev_index_positions(struct hashfile *f,
+				      struct pack_idx_entry **objects,
+				      uint32_t nr_objects)
+{
+	uint32_t *pack_order;
+	uint32_t i;
+
+	ALLOC_ARRAY(pack_order, nr_objects);
+	for (i = 0; i < nr_objects; i++)
+		pack_order[i] = i;
+	QSORT_S(pack_order, nr_objects, pack_order_cmp, objects);
+
+	for (i = 0; i < nr_objects; i++)
+		hashwrite_be32(f, pack_order[i]);
+
+	free(pack_order);
+}
+
+static void write_rev_trailer(struct hashfile *f, const unsigned char *hash)
+{
+	hashwrite(f, hash, the_hash_algo->rawsz);
+}
+
+const char *write_rev_file(const char *rev_name,
+			   struct pack_idx_entry **objects,
+			   uint32_t nr_objects,
+			   const unsigned char *hash,
+			   unsigned flags)
+{
+	struct hashfile *f;
+	int fd;
+
+	if ((flags & WRITE_REV) && (flags & WRITE_REV_VERIFY))
+		die(_("cannot both write and verify reverse index"));
+
+	if (flags & WRITE_REV) {
+		if (!rev_name) {
+			struct strbuf tmp_file = STRBUF_INIT;
+			fd = odb_mkstemp(&tmp_file, "pack/tmp_rev_XXXXXX");
+			rev_name = strbuf_detach(&tmp_file, NULL);
+		} else {
+			unlink(rev_name);
+			fd = open(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+			if (fd < 0)
+				die_errno("unable to create '%s'", rev_name);
+		}
+		f = hashfd(fd, rev_name);
+	} else if (flags & WRITE_REV_VERIFY) {
+		struct stat statbuf;
+		if (stat(rev_name, &statbuf)) {
+			if (errno == ENOENT) {
+				/* .rev files are optional */
+				return NULL;
+			} else
+				die_errno(_("could not stat: %s"), rev_name);
+		}
+		f = hashfd_check(rev_name);
+	} else
+		return NULL;
+
+	write_rev_header(f);
+
+	write_rev_index_positions(f, objects, nr_objects);
+	write_rev_trailer(f, hash);
+
+	if (rev_name && adjust_shared_perm(rev_name) < 0)
+		die(_("failed to make %s readable"), rev_name);
+
+	finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_CLOSE |
+				    ((flags & WRITE_IDX_VERIFY) ? 0 : CSUM_FSYNC));
+
+	return rev_name;
+}
+
 off_t write_pack_header(struct hashfile *f, uint32_t nr_entries)
 {
 	struct pack_header hdr;
@@ -272,7 +380,7 @@
 	fsync_or_die(pack_fd, pack_name);
 }
 
-char *index_pack_lockfile(int ip_out)
+char *index_pack_lockfile(int ip_out, int *is_well_formed)
 {
 	char packname[GIT_MAX_HEXSZ + 6];
 	const int len = the_hash_algo->hexsz + 6;
@@ -286,11 +394,17 @@
 	 */
 	if (read_in_full(ip_out, packname, len) == len && packname[len-1] == '\n') {
 		const char *name;
+
+		if (is_well_formed)
+			*is_well_formed = 1;
 		packname[len-1] = 0;
 		if (skip_prefix(packname, "keep\t", &name))
 			return xstrfmt("%s/pack/pack-%s.keep",
 				       get_object_directory(), name);
+		return NULL;
 	}
+	if (is_well_formed)
+		*is_well_formed = 0;
 	return NULL;
 }
 
@@ -341,7 +455,7 @@
 			 struct pack_idx_option *pack_idx_opts,
 			 unsigned char hash[])
 {
-	const char *idx_tmp_name;
+	const char *idx_tmp_name, *rev_tmp_name = NULL;
 	int basename_len = name_buffer->len;
 
 	if (adjust_shared_perm(pack_tmp_name))
@@ -352,6 +466,9 @@
 	if (adjust_shared_perm(idx_tmp_name))
 		die_errno("unable to make temporary index file readable");
 
+	rev_tmp_name = write_rev_file(NULL, written_list, nr_written, hash,
+				      pack_idx_opts->flags);
+
 	strbuf_addf(name_buffer, "%s.pack", hash_to_hex(hash));
 
 	if (rename(pack_tmp_name, name_buffer->buf))
@@ -365,5 +482,28 @@
 
 	strbuf_setlen(name_buffer, basename_len);
 
+	if (rev_tmp_name) {
+		strbuf_addf(name_buffer, "%s.rev", hash_to_hex(hash));
+		if (rename(rev_tmp_name, name_buffer->buf))
+			die_errno("unable to rename temporary reverse-index file");
+	}
+
+	strbuf_setlen(name_buffer, basename_len);
+
 	free((void *)idx_tmp_name);
 }
+
+void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought)
+{
+	int i, err;
+	FILE *output = xfopen(promisor_name, "w");
+
+	for (i = 0; i < nr_sought; i++)
+		fprintf(output, "%s %s\n", oid_to_hex(&sought[i]->old_oid),
+			sought[i]->name);
+
+	err = ferror(output);
+	err |= fclose(output);
+	if (err)
+		die(_("could not write '%s' promisor file"), promisor_name);
+}
diff --git a/pack.h b/pack.h
index 9fc0945..857cbd5 100644
--- a/pack.h
+++ b/pack.h
@@ -42,6 +42,8 @@
 	/* flag bits */
 #define WRITE_IDX_VERIFY 01 /* verify only, do not write the idx file */
 #define WRITE_IDX_STRICT 02
+#define WRITE_REV 04
+#define WRITE_REV_VERIFY 010
 
 	uint32_t version;
 	uint32_t off32_limit;
@@ -85,7 +87,13 @@
 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);
+char *index_pack_lockfile(int fd, int *is_well_formed);
+
+struct ref;
+
+void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought);
+
+const char *write_rev_file(const char *rev_name, struct pack_idx_entry **objects, uint32_t nr_objects, const unsigned char *hash, unsigned flags);
 
 /*
  * The "hdr" output buffer should be at least this big, which will handle sizes
diff --git a/packfile.c b/packfile.c
index 86f5c8d..1fec12a 100644
--- a/packfile.c
+++ b/packfile.c
@@ -7,7 +7,7 @@
 #include "packfile.h"
 #include "delta.h"
 #include "streaming.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "commit.h"
 #include "object.h"
 #include "tag.h"
@@ -324,11 +324,21 @@
 	}
 }
 
+void close_pack_revindex(struct packed_git *p) {
+	if (!p->revindex_map)
+		return;
+
+	munmap((void *)p->revindex_map, p->revindex_size);
+	p->revindex_map = NULL;
+	p->revindex_data = NULL;
+}
+
 void close_pack(struct packed_git *p)
 {
 	close_pack_windows(p);
 	close_pack_fd(p);
 	close_pack_index(p);
+	close_pack_revindex(p);
 }
 
 void close_object_store(struct raw_object_store *o)
@@ -351,7 +361,7 @@
 
 void unlink_pack_path(const char *pack_name, int force_delete)
 {
-	static const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
+	static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor"};
 	int i;
 	struct strbuf buf = STRBUF_INIT;
 	size_t plen;
@@ -853,6 +863,7 @@
 	if (!strcmp(file_name, "multi-pack-index"))
 		return;
 	if (ends_with(file_name, ".idx") ||
+	    ends_with(file_name, ".rev") ||
 	    ends_with(file_name, ".pack") ||
 	    ends_with(file_name, ".bitmap") ||
 	    ends_with(file_name, ".keep") ||
@@ -1235,18 +1246,18 @@
 		oidread(oid, base);
 		return 0;
 	} else if (type == OBJ_OFS_DELTA) {
-		struct revindex_entry *revidx;
+		uint32_t base_pos;
 		off_t base_offset = get_delta_base(p, w_curs, &curpos,
 						   type, delta_obj_offset);
 
 		if (!base_offset)
 			return -1;
 
-		revidx = find_pack_revindex(p, base_offset);
-		if (!revidx)
+		if (offset_to_pack_pos(p, base_offset, &base_pos) < 0)
 			return -1;
 
-		return nth_packed_object_id(oid, p, revidx->nr);
+		return nth_packed_object_id(oid, p,
+					    pack_pos_to_index(p, base_pos));
 	} else
 		return -1;
 }
@@ -1256,12 +1267,11 @@
 				   off_t obj_offset)
 {
 	int type;
-	struct revindex_entry *revidx;
+	uint32_t pos;
 	struct object_id oid;
-	revidx = find_pack_revindex(p, obj_offset);
-	if (!revidx)
+	if (offset_to_pack_pos(p, obj_offset, &pos) < 0)
 		return OBJ_BAD;
-	nth_packed_object_id(&oid, p, revidx->nr);
+	nth_packed_object_id(&oid, p, pack_pos_to_index(p, pos));
 	mark_bad_packed_object(p, oid.hash);
 	type = oid_object_info(r, &oid, NULL);
 	if (type <= OBJ_NONE)
@@ -1538,8 +1548,15 @@
 	}
 
 	if (oi->disk_sizep) {
-		struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
-		*oi->disk_sizep = revidx[1].offset - obj_offset;
+		uint32_t pos;
+		if (offset_to_pack_pos(p, obj_offset, &pos) < 0) {
+			error("could not find object at offset %"PRIuMAX" "
+			      "in pack %s", (uintmax_t)obj_offset, p->pack_name);
+			type = OBJ_BAD;
+			goto out;
+		}
+
+		*oi->disk_sizep = pack_pos_to_offset(p, pos + 1) - obj_offset;
 	}
 
 	if (oi->typep || oi->type_name) {
@@ -1688,11 +1705,21 @@
 		}
 
 		if (do_check_packed_object_crc && p->index_version > 1) {
-			struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
-			off_t len = revidx[1].offset - obj_offset;
-			if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+			uint32_t pack_pos, index_pos;
+			off_t len;
+
+			if (offset_to_pack_pos(p, obj_offset, &pack_pos) < 0) {
+				error("could not find object at offset %"PRIuMAX" in pack %s",
+				      (uintmax_t)obj_offset, p->pack_name);
+				data = NULL;
+				goto out;
+			}
+
+			len = pack_pos_to_offset(p, pack_pos + 1) - obj_offset;
+			index_pos = pack_pos_to_index(p, pack_pos);
+			if (check_pack_crc(p, &w_curs, obj_offset, len, index_pos)) {
 				struct object_id oid;
-				nth_packed_object_id(&oid, p, revidx->nr);
+				nth_packed_object_id(&oid, p, index_pos);
 				error("bad packed object CRC for %s",
 				      oid_to_hex(&oid));
 				mark_bad_packed_object(p, oid.hash);
@@ -1775,11 +1802,11 @@
 			 * This is costly but should happen only in the presence
 			 * of a corrupted pack, and is better than failing outright.
 			 */
-			struct revindex_entry *revidx;
+			uint32_t pos;
 			struct object_id base_oid;
-			revidx = find_pack_revindex(p, obj_offset);
-			if (revidx) {
-				nth_packed_object_id(&base_oid, p, revidx->nr);
+			if (!(offset_to_pack_pos(p, obj_offset, &pos))) {
+				nth_packed_object_id(&base_oid, p,
+						     pack_pos_to_index(p, pos));
 				error("failed to read delta base object %s"
 				      " at offset %"PRIuMAX" from %s",
 				      oid_to_hex(&base_oid), (uintmax_t)obj_offset,
@@ -2066,19 +2093,31 @@
 	}
 
 	for (i = 0; i < p->num_objects; i++) {
-		uint32_t pos;
+		uint32_t index_pos;
 		struct object_id oid;
 
+		/*
+		 * We are iterating "i" from 0 up to num_objects, but its
+		 * meaning may be different, depending on the requested output
+		 * order:
+		 *
+		 *   - in object-name order, it is the same as the index order
+		 *     used by nth_packed_object_id(), so we can pass it
+		 *     directly
+		 *
+		 *   - in pack-order, it is pack position, which we must
+		 *     convert to an index position in order to get the oid.
+		 */
 		if (flags & FOR_EACH_OBJECT_PACK_ORDER)
-			pos = p->revindex[i].nr;
+			index_pos = pack_pos_to_index(p, i);
 		else
-			pos = i;
+			index_pos = i;
 
-		if (nth_packed_object_id(&oid, p, pos) < 0)
+		if (nth_packed_object_id(&oid, p, index_pos) < 0)
 			return error("unable to get sha1 of object %u in %s",
-				     pos, p->pack_name);
+				     index_pos, p->pack_name);
 
-		r = cb(&oid, p, pos, data);
+		r = cb(&oid, p, index_pos, data);
 		if (r)
 			break;
 	}
diff --git a/packfile.h b/packfile.h
index a58fc73..4cfec9e 100644
--- a/packfile.h
+++ b/packfile.h
@@ -90,6 +90,7 @@
 
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
+void close_pack_revindex(struct packed_git *);
 void close_pack(struct packed_git *);
 void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
diff --git a/pager.c b/pager.c
index ee435de..3d37dd7 100644
--- a/pager.c
+++ b/pager.c
@@ -11,29 +11,25 @@
 static struct child_process pager_process = CHILD_PROCESS_INIT;
 static const char *pager_program;
 
-static void wait_for_pager(int in_signal)
+static void close_pager_fds(void)
 {
-	if (!in_signal) {
-		fflush(stdout);
-		fflush(stderr);
-	}
 	/* signal EOF to pager */
 	close(1);
 	close(2);
-	if (in_signal)
-		finish_command_in_signal(&pager_process);
-	else
-		finish_command(&pager_process);
 }
 
 static void wait_for_pager_atexit(void)
 {
-	wait_for_pager(0);
+	fflush(stdout);
+	fflush(stderr);
+	close_pager_fds();
+	finish_command(&pager_process);
 }
 
 static void wait_for_pager_signal(int signo)
 {
-	wait_for_pager(1);
+	close_pager_fds();
+	finish_command_in_signal(&pager_process);
 	sigchain_pop(signo);
 	raise(signo);
 }
diff --git a/parse-options.c b/parse-options.c
index f050743..fbea16e 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -869,7 +869,7 @@
 		usage_with_options(usagestr, options);
 	}
 
-	precompose_argv(argc, argv);
+	precompose_argv_prefix(argc, argv, NULL);
 	free(real_options);
 	free(ctx.alias_groups);
 	return parse_options_end(&ctx);
diff --git a/patch-ids.c b/patch-ids.c
index f51021a..3f404e4 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
 #include "patch-ids.h"
 
 static int patch_id_defined(struct commit *commit)
diff --git a/po/README b/po/README
index 07595d3..efd5baa 100644
--- a/po/README
+++ b/po/README
@@ -284,23 +284,5 @@
 Testing marked strings
 ----------------------
 
-Even if you've correctly marked porcelain strings for translation
-something in the test suite might still depend on the US English
-version of the strings, e.g. to grep some error message or other
-output.
-
-To smoke out issues like these, Git tested with a translation mode that
-emits gibberish on every call to gettext. To use it run the test suite
-with it, e.g.:
-
-    cd t && GIT_TEST_GETTEXT_POISON=true prove -j 9 ./t[0-9]*.sh
-
-If tests break with it you should inspect them manually and see if
-what you're translating is sane, i.e. that you're not translating
-plumbing output.
-
-If not you should replace calls to grep with test_i18ngrep, or
-test_cmp calls with test_i18ncmp. If that's not enough you can skip
-the whole test by making it depend on the C_LOCALE_OUTPUT
-prerequisite. See existing test files with this prerequisite for
-examples.
+Git's tests are run under LANG=C LC_ALL=C. So the tests do not need be
+changed to account for translations as they're added.
diff --git a/preload-index.c b/preload-index.c
index ed6eaa4..e5529a5 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -31,6 +31,7 @@
 	struct pathspec pathspec;
 	struct progress_data *progress;
 	int offset, nr;
+	int t2_nr_lstat;
 };
 
 static void *preload_thread(void *_data)
@@ -73,6 +74,7 @@
 			continue;
 		if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
 			continue;
+		p->t2_nr_lstat++;
 		if (lstat(ce->name, &st))
 			continue;
 		if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))
@@ -98,6 +100,7 @@
 	int threads, i, work, offset;
 	struct thread_data data[MAX_PARALLEL];
 	struct progress_data pd;
+	int t2_sum_lstat = 0;
 
 	if (!HAVE_THREADS || !core_preload_index)
 		return;
@@ -107,6 +110,9 @@
 		threads = 2;
 	if (threads < 2)
 		return;
+
+	trace2_region_enter("index", "preload", NULL);
+
 	trace_performance_enter();
 	if (threads > MAX_PARALLEL)
 		threads = MAX_PARALLEL;
@@ -141,10 +147,14 @@
 		struct thread_data *p = data+i;
 		if (pthread_join(p->pthread, NULL))
 			die("unable to join threaded lstat");
+		t2_sum_lstat += p->t2_nr_lstat;
 	}
 	stop_progress(&pd.progress);
 
 	trace_performance_leave("preload index");
+
+	trace2_data_intmax("index", NULL, "preload/sum_lstat", t2_sum_lstat);
+	trace2_region_leave("index", "preload", NULL);
 }
 
 int repo_read_index_preload(struct repository *repo,
diff --git a/pretty.c b/pretty.c
index 7a7708a..c5f5ecc 100644
--- a/pretty.c
+++ b/pretty.c
@@ -679,7 +679,7 @@
 	static struct string_list *mail_map;
 	if (!mail_map) {
 		mail_map = xcalloc(1, sizeof(*mail_map));
-		read_mailmap(mail_map, NULL);
+		read_mailmap(mail_map);
 	}
 	return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
 }
@@ -783,6 +783,7 @@
 };
 
 struct format_commit_context {
+	struct repository *repository;
 	const struct commit *commit;
 	const struct pretty_print_context *pretty_ctx;
 	unsigned commit_header_parsed:1;
@@ -1148,6 +1149,63 @@
 	return 0;
 }
 
+int format_set_trailers_options(struct process_trailer_options *opts,
+				struct string_list *filter_list,
+				struct strbuf *sepbuf,
+				struct strbuf *kvsepbuf,
+				const char **arg,
+				char **invalid_arg)
+{
+	for (;;) {
+		const char *argval;
+		size_t arglen;
+
+		if (**arg == ')')
+			break;
+
+		if (match_placeholder_arg_value(*arg, "key", arg, &argval, &arglen)) {
+			uintptr_t len = arglen;
+
+			if (!argval)
+				return -1;
+
+			if (len && argval[len - 1] == ':')
+				len--;
+			string_list_append(filter_list, argval)->util = (char *)len;
+
+			opts->filter = format_trailer_match_cb;
+			opts->filter_data = filter_list;
+			opts->only_trailers = 1;
+		} else if (match_placeholder_arg_value(*arg, "separator", arg, &argval, &arglen)) {
+			char *fmt;
+
+			strbuf_reset(sepbuf);
+			fmt = xstrndup(argval, arglen);
+			strbuf_expand(sepbuf, fmt, strbuf_expand_literal_cb, NULL);
+			free(fmt);
+			opts->separator = sepbuf;
+		} else if (match_placeholder_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) {
+			char *fmt;
+
+			strbuf_reset(kvsepbuf);
+			fmt = xstrndup(argval, arglen);
+			strbuf_expand(kvsepbuf, fmt, strbuf_expand_literal_cb, NULL);
+			free(fmt);
+			opts->key_value_separator = kvsepbuf;
+		} else if (!match_placeholder_bool_arg(*arg, "only", arg, &opts->only_trailers) &&
+			   !match_placeholder_bool_arg(*arg, "unfold", arg, &opts->unfold) &&
+			   !match_placeholder_bool_arg(*arg, "keyonly", arg, &opts->key_only) &&
+			   !match_placeholder_bool_arg(*arg, "valueonly", arg, &opts->value_only)) {
+			if (invalid_arg) {
+				size_t len = strcspn(*arg, ",)");
+				*invalid_arg = xstrndup(*arg, len);
+			}
+			return -1;
+		}
+	}
+	return 0;
+}
+
 static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 				const char *placeholder,
 				void *context)
@@ -1373,10 +1431,13 @@
 		return 2;
 	}
 
-
 	/* For the rest we have to parse the commit header. */
-	if (!c->commit_header_parsed)
+	if (!c->commit_header_parsed) {
+		msg = c->message =
+			repo_logmsg_reencode(c->repository, commit,
+					     &c->commit_encoding, "UTF-8");
 		parse_commit_header(c);
+	}
 
 	switch (placeholder[0]) {
 	case 'a':	/* author ... */
@@ -1418,42 +1479,15 @@
 		struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
 		struct string_list filter_list = STRING_LIST_INIT_NODUP;
 		struct strbuf sepbuf = STRBUF_INIT;
+		struct strbuf kvsepbuf = STRBUF_INIT;
 		size_t ret = 0;
 
 		opts.no_divider = 1;
 
 		if (*arg == ':') {
 			arg++;
-			for (;;) {
-				const char *argval;
-				size_t arglen;
-
-				if (match_placeholder_arg_value(arg, "key", &arg, &argval, &arglen)) {
-					uintptr_t len = arglen;
-
-					if (!argval)
-						goto trailer_out;
-
-					if (len && argval[len - 1] == ':')
-						len--;
-					string_list_append(&filter_list, argval)->util = (char *)len;
-
-					opts.filter = format_trailer_match_cb;
-					opts.filter_data = &filter_list;
-					opts.only_trailers = 1;
-				} else if (match_placeholder_arg_value(arg, "separator", &arg, &argval, &arglen)) {
-					char *fmt;
-
-					strbuf_reset(&sepbuf);
-					fmt = xstrndup(argval, arglen);
-					strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
-					free(fmt);
-					opts.separator = &sepbuf;
-				} else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
-					   !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
-					   !match_placeholder_bool_arg(arg, "valueonly", &arg, &opts.value_only))
-					break;
-			}
+			if (format_set_trailers_options(&opts, &filter_list, &sepbuf, &kvsepbuf, &arg, NULL))
+				goto trailer_out;
 		}
 		if (*arg == ')') {
 			format_trailers_from_commit(sb, msg + c->subject_off, &opts);
@@ -1657,6 +1691,7 @@
 				const struct pretty_print_context *pretty_ctx)
 {
 	struct format_commit_context context = {
+		.repository = r,
 		.commit = commit,
 		.pretty_ctx = pretty_ctx,
 		.wrap_start = sb->len
@@ -1664,18 +1699,14 @@
 	const char *output_enc = pretty_ctx->output_encoding;
 	const char *utf8 = "UTF-8";
 
-	/*
-	 * convert a commit message to UTF-8 first
-	 * as far as 'format_commit_item' assumes it in UTF-8
-	 */
-	context.message = repo_logmsg_reencode(r, commit,
-					       &context.commit_encoding,
-					       utf8);
-
 	strbuf_expand(sb, format, format_commit_item, &context);
 	rewrap_message_tail(sb, &context, 0, 0, 0);
 
-	/* then convert a commit message to an actual output encoding */
+	/*
+	 * Convert output to an actual output encoding; note that
+	 * format_commit_item() will always use UTF-8, so we don't
+	 * have to bother if that's what the output wants.
+	 */
 	if (output_enc) {
 		if (same_encoding(utf8, output_enc))
 			output_enc = NULL;
diff --git a/pretty.h b/pretty.h
index 7ce6c0b..d902cdd 100644
--- a/pretty.h
+++ b/pretty.h
@@ -6,6 +6,7 @@
 
 struct commit;
 struct strbuf;
+struct process_trailer_options;
 
 /* Commit formats */
 enum cmit_fmt {
@@ -142,4 +143,15 @@
 /* Make subject of commit message suitable for filename */
 void format_sanitized_subject(struct strbuf *sb, const char *msg, size_t len);
 
+/*
+ * Set values of fields in "struct process_trailer_options"
+ * according to trailers arguments.
+ */
+int format_set_trailers_options(struct process_trailer_options *opts,
+			struct string_list *filter_list,
+			struct strbuf *sepbuf,
+			struct strbuf *kvsepbuf,
+			const char **arg,
+			char **invalid_arg);
+
 #endif /* PRETTY_H */
diff --git a/quote.c b/quote.c
index 69f4ca4..8a3a5e3 100644
--- a/quote.c
+++ b/quote.c
@@ -116,7 +116,7 @@
 	}
 }
 
-static char *sq_dequote_step(char *arg, char **next)
+char *sq_dequote_step(char *arg, char **next)
 {
 	char *dst = arg;
 	char *src = arg;
@@ -153,11 +153,8 @@
 			}
 		/* Fallthrough */
 		default:
-			if (!next || !isspace(*src))
+			if (!next)
 				return NULL;
-			do {
-				c = *++src;
-			} while (isspace(c));
 			*dst = 0;
 			*next = src;
 			return arg;
@@ -182,6 +179,14 @@
 		char *dequoted = sq_dequote_step(next, &next);
 		if (!dequoted)
 			return -1;
+		if (next) {
+			char c;
+			if (!isspace(*next))
+				return -1;
+			do {
+				c = *++next;
+			} while (isspace(c));
+		}
 		if (argv) {
 			ALLOC_GROW(*argv, *nr + 1, *alloc);
 			(*argv)[(*nr)++] = dequoted;
diff --git a/quote.h b/quote.h
index 4b72a58..768cc63 100644
--- a/quote.h
+++ b/quote.h
@@ -42,13 +42,27 @@
 void sq_quote_argv_pretty(struct strbuf *, const char **argv);
 void sq_append_quote_argv_pretty(struct strbuf *dst, const char **argv);
 
-/* This unwraps what sq_quote() produces in place, but returns
+/*
+ * This unwraps what sq_quote() produces in place, but returns
  * NULL if the input does not look like what sq_quote would have
- * produced.
+ * produced (the full string must be a single quoted item).
  */
 char *sq_dequote(char *);
 
 /*
+ * Like sq_dequote(), but dequote a single item, and leave "next" pointing to
+ * the next character. E.g., in the string:
+ *
+ *   'one' 'two' 'three'
+ *
+ * after the first call, the return value would be the unquoted string "one",
+ * with "next" pointing to the space between "one" and "two"). The caller is
+ * responsible for advancing the pointer to the start of the next item before
+ * calling sq_dequote_step() again.
+ */
+char *sq_dequote_step(char *src, char **next);
+
+/*
  * 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.
diff --git a/range-diff.c b/range-diff.c
index b9950f1..a3cc7c9 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -11,6 +11,7 @@
 #include "pretty.h"
 #include "userdiff.h"
 #include "apply.h"
+#include "revision.h"
 
 struct patch_util {
 	/* For the search for an exact match */
@@ -80,6 +81,8 @@
 		finish_command(&cp);
 		return -1;
 	}
+	if (finish_command(&cp))
+		return -1;
 
 	line = contents.buf;
 	size = contents.len;
@@ -97,10 +100,10 @@
 			if (get_oid(p, &util->oid)) {
 				error(_("could not parse commit '%s'"), p);
 				free(util);
+				free(current_filename);
 				string_list_clear(list, 1);
 				strbuf_release(&buf);
 				strbuf_release(&contents);
-				finish_command(&cp);
 				return -1;
 			}
 			util->matching = -1;
@@ -112,10 +115,10 @@
 			error(_("could not parse first line of `log` output: "
 				"did not start with 'commit ': '%s'"),
 			      line);
+			free(current_filename);
 			string_list_clear(list, 1);
 			strbuf_release(&buf);
 			strbuf_release(&contents);
-			finish_command(&cp);
 			return -1;
 		}
 
@@ -133,9 +136,16 @@
 			orig_len = len;
 			len = parse_git_diff_header(&root, &linenr, 0, line,
 						    len, size, &patch);
-			if (len < 0)
-				die(_("could not parse git header '%.*s'"),
-				    orig_len, line);
+			if (len < 0) {
+				error(_("could not parse git header '%.*s'"),
+				      orig_len, line);
+				free(util);
+				free(current_filename);
+				string_list_clear(list, 1);
+				strbuf_release(&buf);
+				strbuf_release(&contents);
+				return -1;
+			}
 			strbuf_addstr(&buf, " ## ");
 			if (patch.is_new > 0)
 				strbuf_addf(&buf, "%s (new)", patch.new_name);
@@ -218,9 +228,6 @@
 	strbuf_release(&buf);
 	free(current_filename);
 
-	if (finish_command(&cp))
-		return -1;
-
 	return 0;
 }
 
@@ -458,12 +465,35 @@
 	diff_flush(diffopt);
 }
 
+static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
+{
+	return data;
+}
+
 static void output(struct string_list *a, struct string_list *b,
-		   struct diff_options *diffopt)
+		   struct range_diff_options *range_diff_opts)
 {
 	struct strbuf buf = STRBUF_INIT, dashes = STRBUF_INIT;
 	int patch_no_width = decimal_width(1 + (a->nr > b->nr ? a->nr : b->nr));
 	int i = 0, j = 0;
+	struct diff_options opts;
+	struct strbuf indent = STRBUF_INIT;
+
+	if (range_diff_opts->diffopt)
+		memcpy(&opts, range_diff_opts->diffopt, sizeof(opts));
+	else
+		diff_setup(&opts);
+
+	if (!opts.output_format)
+		opts.output_format = DIFF_FORMAT_PATCH;
+	opts.flags.suppress_diff_headers = 1;
+	opts.flags.dual_color_diffed_diffs =
+		range_diff_opts->dual_color;
+	opts.flags.suppress_hunk_header_line_count = 1;
+	opts.output_prefix = output_prefix_cb;
+	strbuf_addstr(&indent, "    ");
+	opts.output_prefix_data = &indent;
+	diff_setup_done(&opts);
 
 	/*
 	 * We assume the user is really more interested in the second argument
@@ -484,7 +514,8 @@
 
 		/* Show unmatched LHS commit whose predecessors were shown. */
 		if (i < a->nr && a_util->matching < 0) {
-			output_pair_header(diffopt, patch_no_width,
+			if (!range_diff_opts->right_only)
+				output_pair_header(&opts, patch_no_width,
 					   &buf, &dashes, a_util, NULL);
 			i++;
 			continue;
@@ -492,7 +523,8 @@
 
 		/* Show unmatched RHS commits. */
 		while (j < b->nr && b_util->matching < 0) {
-			output_pair_header(diffopt, patch_no_width,
+			if (!range_diff_opts->left_only)
+				output_pair_header(&opts, patch_no_width,
 					   &buf, &dashes, NULL, b_util);
 			b_util = ++j < b->nr ? b->items[j].util : NULL;
 		}
@@ -500,63 +532,41 @@
 		/* Show matching LHS/RHS pair. */
 		if (j < b->nr) {
 			a_util = a->items[b_util->matching].util;
-			output_pair_header(diffopt, patch_no_width,
+			output_pair_header(&opts, patch_no_width,
 					   &buf, &dashes, a_util, b_util);
-			if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT))
+			if (!(opts.output_format & DIFF_FORMAT_NO_OUTPUT))
 				patch_diff(a->items[b_util->matching].string,
-					   b->items[j].string, diffopt);
+					   b->items[j].string, &opts);
 			a_util->shown = 1;
 			j++;
 		}
 	}
 	strbuf_release(&buf);
 	strbuf_release(&dashes);
-}
-
-static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
-{
-	return data;
+	strbuf_release(&indent);
 }
 
 int show_range_diff(const char *range1, const char *range2,
-		    int creation_factor, int dual_color,
-		    const struct diff_options *diffopt,
-		    const struct strvec *other_arg)
+		    struct range_diff_options *range_diff_opts)
 {
 	int res = 0;
 
 	struct string_list branch1 = STRING_LIST_INIT_DUP;
 	struct string_list branch2 = STRING_LIST_INIT_DUP;
 
-	if (read_patches(range1, &branch1, other_arg))
+	if (range_diff_opts->left_only && range_diff_opts->right_only)
+		res = error(_("--left-only and --right-only are mutually exclusive"));
+
+	if (!res && read_patches(range1, &branch1, range_diff_opts->other_arg))
 		res = error(_("could not parse log for '%s'"), range1);
-	if (!res && read_patches(range2, &branch2, other_arg))
+	if (!res && read_patches(range2, &branch2, range_diff_opts->other_arg))
 		res = error(_("could not parse log for '%s'"), range2);
 
 	if (!res) {
-		struct diff_options opts;
-		struct strbuf indent = STRBUF_INIT;
-
-		if (diffopt)
-			memcpy(&opts, diffopt, sizeof(opts));
-		else
-			diff_setup(&opts);
-
-		if (!opts.output_format)
-			opts.output_format = DIFF_FORMAT_PATCH;
-		opts.flags.suppress_diff_headers = 1;
-		opts.flags.dual_color_diffed_diffs = dual_color;
-		opts.flags.suppress_hunk_header_line_count = 1;
-		opts.output_prefix = output_prefix_cb;
-		strbuf_addstr(&indent, "    ");
-		opts.output_prefix_data = &indent;
-		diff_setup_done(&opts);
-
 		find_exact_matches(&branch1, &branch2);
-		get_correspondences(&branch1, &branch2, creation_factor);
-		output(&branch1, &branch2, &opts);
-
-		strbuf_release(&indent);
+		get_correspondences(&branch1, &branch2,
+				    range_diff_opts->creation_factor);
+		output(&branch1, &branch2, range_diff_opts);
 	}
 
 	string_list_clear(&branch1, 1);
@@ -564,3 +574,31 @@
 
 	return res;
 }
+
+int is_range_diff_range(const char *arg)
+{
+	char *copy = xstrdup(arg); /* setup_revisions() modifies it */
+	const char *argv[] = { "", copy, "--", NULL };
+	int i, positive = 0, negative = 0;
+	struct rev_info revs;
+
+	init_revisions(&revs, NULL);
+	if (setup_revisions(3, argv, &revs, NULL) == 1) {
+		for (i = 0; i < revs.pending.nr; i++)
+			if (revs.pending.objects[i].item->flags & UNINTERESTING)
+				negative++;
+			else
+				positive++;
+		for (i = 0; i < revs.pending.nr; i++) {
+			struct object *obj = revs.pending.objects[i].item;
+
+			if (obj->type == OBJ_COMMIT)
+				clear_commit_marks((struct commit *)obj,
+						   ALL_REV_FLAGS);
+		}
+	}
+
+	free(copy);
+	object_array_clear(&revs.pending);
+	return negative > 0 && positive > 0;
+}
diff --git a/range-diff.h b/range-diff.h
index 583ced2..04ffe21 100644
--- a/range-diff.h
+++ b/range-diff.h
@@ -6,14 +6,25 @@
 
 #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
 
+struct range_diff_options {
+	int creation_factor;
+	unsigned dual_color:1;
+	unsigned left_only:1, right_only:1;
+	const struct diff_options *diffopt; /* may be NULL */
+	const struct strvec *other_arg; /* may be NULL */
+};
+
 /*
- * Compare series of commits in RANGE1 and RANGE2, and emit to the
- * standard output.  NULL can be passed to DIFFOPT to use the built-in
- * default.
+ * Compare series of commits in `range1` and `range2`, and emit to the
+ * standard output.
  */
 int show_range_diff(const char *range1, const char *range2,
-		    int creation_factor, int dual_color,
-		    const struct diff_options *diffopt,
-		    const struct strvec *other_arg);
+		    struct range_diff_options *opts);
+
+/*
+ * Determine whether the given argument is usable as a range argument of `git
+ * range-diff`, e.g. A..B.
+ */
+int is_range_diff_range(const char *arg);
 
 #endif
diff --git a/read-cache.c b/read-cache.c
index 29144cf..1e9a50c 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1364,7 +1364,9 @@
 static struct cache_entry *refresh_cache_ent(struct index_state *istate,
 					     struct cache_entry *ce,
 					     unsigned int options, int *err,
-					     int *changed_ret)
+					     int *changed_ret,
+					     int *t2_did_lstat,
+					     int *t2_did_scan)
 {
 	struct stat st;
 	struct cache_entry *updated;
@@ -1406,6 +1408,8 @@
 		return NULL;
 	}
 
+	if (t2_did_lstat)
+		*t2_did_lstat = 1;
 	if (lstat(ce->name, &st) < 0) {
 		if (ignore_missing && errno == ENOENT)
 			return ce;
@@ -1442,6 +1446,8 @@
 		}
 	}
 
+	if (t2_did_scan)
+		*t2_did_scan = 1;
 	if (ie_modified(istate, ce, &st, options)) {
 		if (err)
 			*err = EINVAL;
@@ -1519,6 +1525,8 @@
 	const char *added_fmt;
 	const char *unmerged_fmt;
 	struct progress *progress = NULL;
+	int t2_sum_lstat = 0;
+	int t2_sum_scan = 0;
 
 	if (flags & REFRESH_PROGRESS && isatty(2))
 		progress = start_delayed_progress(_("Refresh index"),
@@ -1536,11 +1544,14 @@
 	 * we only have to do the special cases that are left.
 	 */
 	preload_index(istate, pathspec, 0);
+	trace2_region_enter("index", "refresh", NULL);
 	for (i = 0; i < istate->cache_nr; i++) {
 		struct cache_entry *ce, *new_entry;
 		int cache_errno = 0;
 		int changed = 0;
 		int filtered = 0;
+		int t2_did_lstat = 0;
+		int t2_did_scan = 0;
 
 		ce = istate->cache[i];
 		if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
@@ -1566,7 +1577,11 @@
 		if (filtered)
 			continue;
 
-		new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
+		new_entry = refresh_cache_ent(istate, ce, options,
+					      &cache_errno, &changed,
+					      &t2_did_lstat, &t2_did_scan);
+		t2_sum_lstat += t2_did_lstat;
+		t2_sum_scan += t2_did_scan;
 		if (new_entry == ce)
 			continue;
 		if (progress)
@@ -1602,6 +1617,9 @@
 
 		replace_index_entry(istate, i, new_entry);
 	}
+	trace2_data_intmax("index", NULL, "refresh/sum_lstat", t2_sum_lstat);
+	trace2_data_intmax("index", NULL, "refresh/sum_scan", t2_sum_scan);
+	trace2_region_leave("index", "refresh", NULL);
 	if (progress) {
 		display_progress(progress, istate->cache_nr);
 		stop_progress(&progress);
@@ -1614,7 +1632,7 @@
 					struct cache_entry *ce,
 					unsigned int options)
 {
-	return refresh_cache_ent(istate, ce, options, NULL, NULL);
+	return refresh_cache_ent(istate, ce, options, NULL, NULL, NULL, NULL);
 }
 
 
@@ -2447,7 +2465,7 @@
 	}
 }
 
-#define WRITE_BUFFER_SIZE 8192
+#define WRITE_BUFFER_SIZE (128 * 1024)
 static unsigned char write_buffer[WRITE_BUFFER_SIZE];
 static unsigned long write_buffer_len;
 
diff --git a/ref-filter.c b/ref-filter.c
index ee337df..e84efb5 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -67,6 +67,12 @@
 	int lstrip, rstrip;
 };
 
+static struct ref_trailer_buf {
+	struct string_list filter_list;
+	struct strbuf sepbuf;
+	struct strbuf kvsepbuf;
+} ref_trailer_buf = {STRING_LIST_INIT_NODUP, STRBUF_INIT, STRBUF_INIT};
+
 static struct expand_data {
 	struct object_id oid;
 	enum object_type type;
@@ -313,28 +319,26 @@
 static int trailers_atom_parser(const struct ref_format *format, struct used_atom *atom,
 				const char *arg, struct strbuf *err)
 {
-	struct string_list params = STRING_LIST_INIT_DUP;
-	int i;
-
 	atom->u.contents.trailer_opts.no_divider = 1;
 
 	if (arg) {
-		string_list_split(&params, arg, ',', -1);
-		for (i = 0; i < params.nr; i++) {
-			const char *s = params.items[i].string;
-			if (!strcmp(s, "unfold"))
-				atom->u.contents.trailer_opts.unfold = 1;
-			else if (!strcmp(s, "only"))
-				atom->u.contents.trailer_opts.only_trailers = 1;
-			else {
-				strbuf_addf(err, _("unknown %%(trailers) argument: %s"), s);
-				string_list_clear(&params, 0);
-				return -1;
-			}
+		const char *argbuf = xstrfmt("%s)", arg);
+		char *invalid_arg = NULL;
+
+		if (format_set_trailers_options(&atom->u.contents.trailer_opts,
+		    &ref_trailer_buf.filter_list,
+		    &ref_trailer_buf.sepbuf,
+		    &ref_trailer_buf.kvsepbuf,
+		    &argbuf, &invalid_arg)) {
+			if (!invalid_arg)
+				strbuf_addf(err, _("expected %%(trailers:key=<value>)"));
+			else
+				strbuf_addf(err, _("unknown %%(trailers) argument: %s"), invalid_arg);
+			free((char *)invalid_arg);
+			return -1;
 		}
 	}
 	atom->u.contents.option = C_TRAILERS;
-	string_list_clear(&params, 0);
 	return 0;
 }
 
@@ -1210,12 +1214,20 @@
 }
 
 static void find_subpos(const char *buf,
-			const char **sub, unsigned long *sublen,
-			const char **body, unsigned long *bodylen,
-			unsigned long *nonsiglen,
-			const char **sig, unsigned long *siglen)
+			const char **sub, size_t *sublen,
+			const char **body, size_t *bodylen,
+			size_t *nonsiglen,
+			const char **sig, size_t *siglen)
 {
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
 	const char *eol;
+	const char *end = buf + strlen(buf);
+	const char *sigstart;
+
+	/* parse signature first; we might not even have a subject line */
+	parse_signature(buf, end - buf, &payload, &signature);
+
 	/* skip past header until we hit empty line */
 	while (*buf && *buf != '\n') {
 		eol = strchrnul(buf, '\n');
@@ -1226,16 +1238,14 @@
 	/* skip any empty lines */
 	while (*buf == '\n')
 		buf++;
-
-	/* parse signature first; we might not even have a subject line */
-	*sig = buf + parse_signature(buf, strlen(buf));
-	*siglen = strlen(*sig);
+	*sig = strbuf_detach(&signature, siglen);
+	sigstart = buf + parse_signed_buffer(buf, strlen(buf));
 
 	/* subject is first non-empty line */
 	*sub = buf;
 	/* subject goes to first empty line before signature begins */
 	if ((eol = strstr(*sub, "\n\n"))) {
-		eol = eol < *sig ? eol : *sig;
+		eol = eol < sigstart ? eol : sigstart;
 	/* check if message uses CRLF */
 	} else if (! (eol = strstr(*sub, "\r\n\r\n"))) {
 		/* treat whole message as subject */
@@ -1253,7 +1263,7 @@
 		buf++;
 	*body = buf;
 	*bodylen = strlen(buf);
-	*nonsiglen = *sig - buf;
+	*nonsiglen = sigstart - buf;
 }
 
 /*
@@ -1285,12 +1295,13 @@
 {
 	int i;
 	const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
-	unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+	size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
 
 	for (i = 0; i < used_atom_cnt; i++) {
 		struct used_atom *atom = &used_atom[i];
 		const char *name = atom->name;
 		struct atom_value *v = &val[i];
+
 		if (!!deref != (*name == '*'))
 			continue;
 		if (deref)
@@ -1322,7 +1333,7 @@
 			v->s = xmemdupz(sigpos, siglen);
 		else if (atom->u.contents.option == C_LINES) {
 			struct strbuf s = STRBUF_INIT;
-			const char *contents_end = bodylen + bodypos - siglen;
+			const char *contents_end = bodypos + nonsiglen;
 
 			/*  Size is the length of the message after removing the signature */
 			append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
@@ -1336,7 +1347,9 @@
 			v->s = strbuf_detach(&s, NULL);
 		} else if (atom->u.contents.option == C_BARE)
 			v->s = xstrdup(subpos);
+
 	}
+	free((void *)sigpos);
 }
 
 /*
@@ -1920,64 +1933,6 @@
 	return match_pattern(filter, refname);
 }
 
-static int qsort_strcmp(const void *va, const void *vb)
-{
-	const char *a = *(const char **)va;
-	const char *b = *(const char **)vb;
-
-	return strcmp(a, b);
-}
-
-static void find_longest_prefixes_1(struct string_list *out,
-				  struct strbuf *prefix,
-				  const char **patterns, size_t nr)
-{
-	size_t i;
-
-	for (i = 0; i < nr; i++) {
-		char c = patterns[i][prefix->len];
-		if (!c || is_glob_special(c)) {
-			string_list_append(out, prefix->buf);
-			return;
-		}
-	}
-
-	i = 0;
-	while (i < nr) {
-		size_t end;
-
-		/*
-		* Set "end" to the index of the element _after_ the last one
-		* in our group.
-		*/
-		for (end = i + 1; end < nr; end++) {
-			if (patterns[i][prefix->len] != patterns[end][prefix->len])
-				break;
-		}
-
-		strbuf_addch(prefix, patterns[i][prefix->len]);
-		find_longest_prefixes_1(out, prefix, patterns + i, end - i);
-		strbuf_setlen(prefix, prefix->len - 1);
-
-		i = end;
-	}
-}
-
-static void find_longest_prefixes(struct string_list *out,
-				  const char **patterns)
-{
-	struct strvec sorted = STRVEC_INIT;
-	struct strbuf prefix = STRBUF_INIT;
-
-	strvec_pushv(&sorted, patterns);
-	QSORT(sorted.v, sorted.nr, qsort_strcmp);
-
-	find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
-
-	strvec_clear(&sorted);
-	strbuf_release(&prefix);
-}
-
 /*
  * This is the same as for_each_fullref_in(), but it tries to iterate
  * only over the patterns we'll care about. Note that it _doesn't_ do a full
@@ -1988,10 +1943,6 @@
 				       void *cb_data,
 				       int broken)
 {
-	struct string_list prefixes = STRING_LIST_INIT_DUP;
-	struct string_list_item *prefix;
-	int ret;
-
 	if (!filter->match_as_path) {
 		/*
 		 * in this case, the patterns are applied after
@@ -2015,16 +1966,8 @@
 		return for_each_fullref_in("", cb, cb_data, broken);
 	}
 
-	find_longest_prefixes(&prefixes, filter->name_patterns);
-
-	for_each_string_list_item(prefix, &prefixes) {
-		ret = for_each_fullref_in(prefix->string, cb, cb_data, broken);
-		if (ret)
-			break;
-	}
-
-	string_list_clear(&prefixes, 0);
-	return ret;
+	return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
+					    cb, cb_data, broken);
 }
 
 /*
diff --git a/refs.c b/refs.c
index 13dc2c3..a665ed5 100644
--- a/refs.c
+++ b/refs.c
@@ -882,59 +882,9 @@
 	int *cutoff_cnt;
 };
 
-static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, timestamp_t timestamp, int tz,
-		const char *message, void *cb_data)
+static void set_read_ref_cutoffs(struct read_ref_at_cb *cb,
+		timestamp_t timestamp, int tz, const char *message)
 {
-	struct read_ref_at_cb *cb = cb_data;
-
-	cb->reccnt++;
-	cb->tz = tz;
-	cb->date = timestamp;
-
-	if (timestamp <= cb->at_time || cb->cnt == 0) {
-		if (cb->msg)
-			*cb->msg = xstrdup(message);
-		if (cb->cutoff_time)
-			*cb->cutoff_time = timestamp;
-		if (cb->cutoff_tz)
-			*cb->cutoff_tz = tz;
-		if (cb->cutoff_cnt)
-			*cb->cutoff_cnt = cb->reccnt - 1;
-		/*
-		 * we have not yet updated cb->[n|o]oid so they still
-		 * hold the values for the previous record.
-		 */
-		if (!is_null_oid(&cb->ooid)) {
-			oidcpy(cb->oid, noid);
-			if (!oideq(&cb->ooid, noid))
-				warning(_("log for ref %s has gap after %s"),
-					cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
-		}
-		else if (cb->date == cb->at_time)
-			oidcpy(cb->oid, noid);
-		else if (!oideq(noid, cb->oid))
-			warning(_("log for ref %s unexpectedly ended on %s"),
-				cb->refname, show_date(cb->date, cb->tz,
-						       DATE_MODE(RFC2822)));
-		oidcpy(&cb->ooid, ooid);
-		oidcpy(&cb->noid, noid);
-		cb->found_it = 1;
-		return 1;
-	}
-	oidcpy(&cb->ooid, ooid);
-	oidcpy(&cb->noid, noid);
-	if (cb->cnt > 0)
-		cb->cnt--;
-	return 0;
-}
-
-static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, timestamp_t timestamp,
-				  int tz, const char *message, void *cb_data)
-{
-	struct read_ref_at_cb *cb = cb_data;
-
 	if (cb->msg)
 		*cb->msg = xstrdup(message);
 	if (cb->cutoff_time)
@@ -943,6 +893,69 @@
 		*cb->cutoff_tz = tz;
 	if (cb->cutoff_cnt)
 		*cb->cutoff_cnt = cb->reccnt;
+}
+
+static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
+		const char *email, timestamp_t timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	struct read_ref_at_cb *cb = cb_data;
+	int reached_count;
+
+	cb->tz = tz;
+	cb->date = timestamp;
+
+	/*
+	 * It is not possible for cb->cnt == 0 on the first iteration because
+	 * that special case is handled in read_ref_at().
+	 */
+	if (cb->cnt > 0)
+		cb->cnt--;
+	reached_count = cb->cnt == 0 && !is_null_oid(ooid);
+	if (timestamp <= cb->at_time || reached_count) {
+		set_read_ref_cutoffs(cb, timestamp, tz, message);
+		/*
+		 * we have not yet updated cb->[n|o]oid so they still
+		 * hold the values for the previous record.
+		 */
+		if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid))
+			warning(_("log for ref %s has gap after %s"),
+					cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
+		if (reached_count)
+			oidcpy(cb->oid, ooid);
+		else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time)
+			oidcpy(cb->oid, noid);
+		else if (!oideq(noid, cb->oid))
+			warning(_("log for ref %s unexpectedly ended on %s"),
+				cb->refname, show_date(cb->date, cb->tz,
+						       DATE_MODE(RFC2822)));
+		cb->found_it = 1;
+	}
+	cb->reccnt++;
+	oidcpy(&cb->ooid, ooid);
+	oidcpy(&cb->noid, noid);
+	return cb->found_it;
+}
+
+static int read_ref_at_ent_newest(struct object_id *ooid, struct object_id *noid,
+				  const char *email, timestamp_t timestamp,
+				  int tz, const char *message, void *cb_data)
+{
+	struct read_ref_at_cb *cb = cb_data;
+
+	set_read_ref_cutoffs(cb, timestamp, tz, message);
+	oidcpy(cb->oid, noid);
+	/* We just want the first entry */
+	return 1;
+}
+
+static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
+				  const char *email, timestamp_t timestamp,
+				  int tz, const char *message, void *cb_data)
+{
+	struct read_ref_at_cb *cb = cb_data;
+
+	set_read_ref_cutoffs(cb, timestamp, tz, message);
 	oidcpy(cb->oid, ooid);
 	if (is_null_oid(cb->oid))
 		oidcpy(cb->oid, noid);
@@ -967,6 +980,11 @@
 	cb.cutoff_cnt = cutoff_cnt;
 	cb.oid = oid;
 
+	if (cb.cnt == 0) {
+		refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb);
+		return 0;
+	}
+
 	refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
 
 	if (!cb.reccnt) {
@@ -1546,6 +1564,93 @@
 	return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
 }
 
+static int qsort_strcmp(const void *va, const void *vb)
+{
+	const char *a = *(const char **)va;
+	const char *b = *(const char **)vb;
+
+	return strcmp(a, b);
+}
+
+static void find_longest_prefixes_1(struct string_list *out,
+				  struct strbuf *prefix,
+				  const char **patterns, size_t nr)
+{
+	size_t i;
+
+	for (i = 0; i < nr; i++) {
+		char c = patterns[i][prefix->len];
+		if (!c || is_glob_special(c)) {
+			string_list_append(out, prefix->buf);
+			return;
+		}
+	}
+
+	i = 0;
+	while (i < nr) {
+		size_t end;
+
+		/*
+		* Set "end" to the index of the element _after_ the last one
+		* in our group.
+		*/
+		for (end = i + 1; end < nr; end++) {
+			if (patterns[i][prefix->len] != patterns[end][prefix->len])
+				break;
+		}
+
+		strbuf_addch(prefix, patterns[i][prefix->len]);
+		find_longest_prefixes_1(out, prefix, patterns + i, end - i);
+		strbuf_setlen(prefix, prefix->len - 1);
+
+		i = end;
+	}
+}
+
+static void find_longest_prefixes(struct string_list *out,
+				  const char **patterns)
+{
+	struct strvec sorted = STRVEC_INIT;
+	struct strbuf prefix = STRBUF_INIT;
+
+	strvec_pushv(&sorted, patterns);
+	QSORT(sorted.v, sorted.nr, qsort_strcmp);
+
+	find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
+
+	strvec_clear(&sorted);
+	strbuf_release(&prefix);
+}
+
+int for_each_fullref_in_prefixes(const char *namespace,
+				 const char **patterns,
+				 each_ref_fn fn, void *cb_data,
+				 unsigned int broken)
+{
+	struct string_list prefixes = STRING_LIST_INIT_DUP;
+	struct string_list_item *prefix;
+	struct strbuf buf = STRBUF_INIT;
+	int ret = 0, namespace_len;
+
+	find_longest_prefixes(&prefixes, patterns);
+
+	if (namespace)
+		strbuf_addstr(&buf, namespace);
+	namespace_len = buf.len;
+
+	for_each_string_list_item(prefix, &prefixes) {
+		strbuf_addstr(&buf, prefix->string);
+		ret = for_each_fullref_in(buf.buf, fn, cb_data, broken);
+		if (ret)
+			break;
+		strbuf_setlen(&buf, namespace_len);
+	}
+
+	string_list_clear(&prefixes, 0);
+	strbuf_release(&buf);
+	return ret;
+}
+
 static int refs_read_special_head(struct ref_store *ref_store,
 				  const char *refname, struct object_id *oid,
 				  struct strbuf *referent, unsigned int *type)
@@ -1898,31 +2003,14 @@
 	return refs->be->pack_refs(refs, flags);
 }
 
-int refs_peel_ref(struct ref_store *refs, const char *refname,
-		  struct object_id *oid)
+int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
 {
-	int flag;
-	struct object_id base;
+	if (current_ref_iter &&
+	    (current_ref_iter->oid == base ||
+	     oideq(current_ref_iter->oid, base)))
+		return ref_iterator_peel(current_ref_iter, peeled);
 
-	if (current_ref_iter && current_ref_iter->refname == refname) {
-		struct object_id peeled;
-
-		if (ref_iterator_peel(current_ref_iter, &peeled))
-			return -1;
-		oidcpy(oid, &peeled);
-		return 0;
-	}
-
-	if (refs_read_ref_full(refs, refname,
-			       RESOLVE_REF_READING, &base, &flag))
-		return -1;
-
-	return peel_object(&base, oid);
-}
-
-int peel_ref(const char *refname, struct object_id *oid)
-{
-	return refs_peel_ref(get_main_ref_store(the_repository), refname, oid);
+	return peel_object(base, peeled);
 }
 
 int refs_create_symref(struct ref_store *refs,
diff --git a/refs.h b/refs.h
index ff05d2e..48970df 100644
--- a/refs.h
+++ b/refs.h
@@ -118,16 +118,16 @@
 int refs_init_db(struct strbuf *err);
 
 /*
- * If refname is a non-symbolic reference that refers to a tag object,
- * and the tag can be (recursively) dereferenced to a non-tag object,
- * store the object ID of the referred-to object to oid and return 0.
- * If any of these conditions are not met, return a non-zero value.
- * Symbolic references are considered unpeelable, even if they
- * ultimately resolve to a peelable tag.
+ * Return the peeled value of the oid currently being iterated via
+ * for_each_ref(), etc. This is equivalent to calling:
+ *
+ *   peel_object(oid, &peeled);
+ *
+ * with the "oid" value given to the each_ref_fn callback, except
+ * that some ref storage may be able to answer the query without
+ * actually loading the object in memory.
  */
-int refs_peel_ref(struct ref_store *refs, const char *refname,
-		  struct object_id *oid);
-int peel_ref(const char *refname, struct object_id *oid);
+int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
 
 /**
  * Resolve refname in the nested "gitlink" repository in the specified
@@ -348,6 +348,15 @@
 			unsigned int broken);
 
 /**
+ * iterate all refs in "patterns" by partitioning patterns into disjoint sets
+ * and iterating the longest-common prefix of each set.
+ *
+ * callers should be prepared to ignore references that they did not ask for.
+ */
+int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
+				 each_ref_fn fn, void *cb_data,
+				 unsigned int broken);
+/**
  * iterate refs from the respective area.
  */
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
diff --git a/remote.h b/remote.h
index 3211abd..5a59198 100644
--- a/remote.h
+++ b/remote.h
@@ -6,6 +6,8 @@
 #include "hashmap.h"
 #include "refspec.h"
 
+struct transport_ls_refs_options;
+
 /**
  * The API gives access to the configuration related to remotes. It handles
  * all three configuration mechanisms historically and currently used by Git,
@@ -134,7 +136,7 @@
 	 * should be 0, so that xcalloc'd structures get it
 	 * by default.
 	 */
-	enum {
+	enum fetch_head_status {
 		FETCH_HEAD_MERGE = -1,
 		FETCH_HEAD_NOT_FOR_MERGE = 0,
 		FETCH_HEAD_IGNORE = 1
@@ -196,7 +198,7 @@
 /* Used for protocol v2 in order to retrieve refs from a remote */
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
 			     struct ref **list, int for_push,
-			     const struct strvec *ref_prefixes,
+			     struct transport_ls_refs_options *transport_options,
 			     const struct string_list *server_options,
 			     int stateless_rpc);
 
diff --git a/repository.c b/repository.c
index a4174dd..c98298a 100644
--- a/repository.c
+++ b/repository.c
@@ -264,6 +264,12 @@
 	if (!repo->index)
 		repo->index = xcalloc(1, sizeof(*repo->index));
 
+	/* Complete the double-reference */
+	if (!repo->index->repo)
+		repo->index->repo = repo;
+	else if (repo->index->repo != repo)
+		BUG("repo's index should point back at itself");
+
 	return read_index_from(repo->index, repo->index_file, repo->gitdir);
 }
 
diff --git a/rerere.c b/rerere.c
index 9281131..dee60dc 100644
--- a/rerere.c
+++ b/rerere.c
@@ -10,7 +10,8 @@
 #include "attr.h"
 #include "pathspec.h"
 #include "object-store.h"
-#include "sha1-lookup.h"
+#include "hash-lookup.h"
+#include "strmap.h"
 
 #define RESOLVED 0
 #define PUNTED 1
@@ -23,26 +24,27 @@
 /* automatically update cleanly resolved paths to the index */
 static int rerere_autoupdate;
 
-static int rerere_dir_nr;
-static int rerere_dir_alloc;
-
 #define RR_HAS_POSTIMAGE 1
 #define RR_HAS_PREIMAGE 2
-static struct rerere_dir {
-	unsigned char hash[GIT_MAX_HEXSZ];
+struct rerere_dir {
 	int status_alloc, status_nr;
 	unsigned char *status;
-} **rerere_dir;
+	char name[FLEX_ARRAY];
+};
+
+static struct strmap rerere_dirs = STRMAP_INIT;
 
 static void free_rerere_dirs(void)
 {
-	int i;
-	for (i = 0; i < rerere_dir_nr; i++) {
-		free(rerere_dir[i]->status);
-		free(rerere_dir[i]);
+	struct hashmap_iter iter;
+	struct strmap_entry *ent;
+
+	strmap_for_each_entry(&rerere_dirs, &iter, ent) {
+		struct rerere_dir *rr_dir = ent->value;
+		free(rr_dir->status);
+		free(rr_dir);
 	}
-	FREE_AND_NULL(rerere_dir);
-	rerere_dir_nr = rerere_dir_alloc = 0;
+	strmap_clear(&rerere_dirs, 0);
 }
 
 static void free_rerere_id(struct string_list_item *item)
@@ -52,7 +54,7 @@
 
 static const char *rerere_id_hex(const struct rerere_id *id)
 {
-	return hash_to_hex(id->collection->hash);
+	return id->collection->name;
 }
 
 static void fit_variant(struct rerere_dir *rr_dir, int variant)
@@ -115,7 +117,7 @@
 static void scan_rerere_dir(struct rerere_dir *rr_dir)
 {
 	struct dirent *de;
-	DIR *dir = opendir(git_path("rr-cache/%s", hash_to_hex(rr_dir->hash)));
+	DIR *dir = opendir(git_path("rr-cache/%s", rr_dir->name));
 
 	if (!dir)
 		return;
@@ -133,39 +135,21 @@
 	closedir(dir);
 }
 
-static const unsigned char *rerere_dir_hash(size_t i, void *table)
-{
-	struct rerere_dir **rr_dir = table;
-	return rr_dir[i]->hash;
-}
-
 static struct rerere_dir *find_rerere_dir(const char *hex)
 {
-	unsigned char hash[GIT_MAX_RAWSZ];
 	struct rerere_dir *rr_dir;
-	int pos;
 
-	if (get_sha1_hex(hex, hash))
-		return NULL; /* BUG */
-	pos = sha1_pos(hash, rerere_dir, rerere_dir_nr, rerere_dir_hash);
-	if (pos < 0) {
-		rr_dir = xmalloc(sizeof(*rr_dir));
-		hashcpy(rr_dir->hash, hash);
+	rr_dir = strmap_get(&rerere_dirs, hex);
+	if (!rr_dir) {
+		FLEX_ALLOC_STR(rr_dir, name, hex);
 		rr_dir->status = NULL;
 		rr_dir->status_nr = 0;
 		rr_dir->status_alloc = 0;
-		pos = -1 - pos;
+		strmap_put(&rerere_dirs, hex, rr_dir);
 
-		/* Make sure the array is big enough ... */
-		ALLOC_GROW(rerere_dir, rerere_dir_nr + 1, rerere_dir_alloc);
-		/* ... and add it in. */
-		rerere_dir_nr++;
-		MOVE_ARRAY(rerere_dir + pos + 1, rerere_dir + pos,
-			   rerere_dir_nr - pos - 1);
-		rerere_dir[pos] = rr_dir;
 		scan_rerere_dir(rr_dir);
 	}
-	return rerere_dir[pos];
+	return rr_dir;
 }
 
 static int has_rerere_resolution(const struct rerere_id *id)
@@ -1178,6 +1162,14 @@
 		unlink_rr_item(id);
 }
 
+/* Does the basename in "path" look plausibly like an rr-cache entry? */
+static int is_rr_cache_dirname(const char *path)
+{
+	struct object_id oid;
+	const char *end;
+	return !parse_oid_hex(path, &oid, &end) && !*end;
+}
+
 void rerere_gc(struct repository *r, struct string_list *rr)
 {
 	struct string_list to_remove = STRING_LIST_INIT_DUP;
@@ -1205,10 +1197,11 @@
 
 		if (is_dot_or_dotdot(e->d_name))
 			continue;
-		rr_dir = find_rerere_dir(e->d_name);
-		if (!rr_dir)
+		if (!is_rr_cache_dirname(e->d_name))
 			continue; /* or should we remove e->d_name? */
 
+		rr_dir = find_rerere_dir(e->d_name);
+
 		now_empty = 1;
 		for (id.variant = 0, id.collection = rr_dir;
 		     id.variant < id.collection->status_nr;
diff --git a/revision.c b/revision.c
index 7ec9b63..b78733f 100644
--- a/revision.c
+++ b/revision.c
@@ -5,6 +5,7 @@
 #include "tree.h"
 #include "commit.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "refs.h"
 #include "revision.h"
 #include "repository.h"
@@ -1808,7 +1809,6 @@
 
 	revs->repo = r;
 	revs->abbrev = DEFAULT_ABBREV;
-	revs->ignore_merges = -1;
 	revs->simplify_history = 1;
 	revs->pruning.repo = r;
 	revs->pruning.flags.recursive = 1;
@@ -2343,34 +2343,8 @@
 		revs->diff = 1;
 		revs->diffopt.flags.recursive = 1;
 		revs->diffopt.flags.tree_in_recursive = 1;
-	} else if (!strcmp(arg, "-m")) {
-		/*
-		 * To "diff-index", "-m" means "match missing", and to the "log"
-		 * family of commands, it means "show full diff for merges". Set
-		 * both fields appropriately.
-		 */
-		revs->ignore_merges = 0;
-		revs->match_missing = 1;
-	} else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
-		if (!strcmp(optarg, "off")) {
-			revs->ignore_merges = 1;
-		} else {
-			die(_("unknown value for --diff-merges: %s"), optarg);
-		}
+	} else if ((argcount = diff_merges_parse_opts(revs, argv))) {
 		return argcount;
-	} else if (!strcmp(arg, "--no-diff-merges")) {
-		revs->ignore_merges = 1;
-	} else if (!strcmp(arg, "-c")) {
-		revs->diff = 1;
-		revs->dense_combined_merges = 0;
-		revs->combine_merges = 1;
-	} else if (!strcmp(arg, "--combined-all-paths")) {
-		revs->diff = 1;
-		revs->combined_all_paths = 1;
-	} else if (!strcmp(arg, "--cc")) {
-		revs->diff = 1;
-		revs->dense_combined_merges = 1;
-		revs->combine_merges = 1;
 	} else if (!strcmp(arg, "-v")) {
 		revs->verbose_header = 1;
 	} else if (!strcmp(arg, "--pretty")) {
@@ -2491,8 +2465,6 @@
 	} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
 		add_message_grep(revs, optarg);
 		return argcount;
-	} else if (!strcmp(arg, "--grep-debug")) {
-		revs->grep_filter.debug = 1;
 	} else if (!strcmp(arg, "--basic-regexp")) {
 		revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_BRE;
 	} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
@@ -2867,12 +2839,8 @@
 			copy_pathspec(&revs->diffopt.pathspec,
 				      &revs->prune_data);
 	}
-	if (revs->combine_merges && revs->ignore_merges < 0)
-		revs->ignore_merges = 0;
-	if (revs->ignore_merges < 0)
-		revs->ignore_merges = 1;
-	if (revs->combined_all_paths && !revs->combine_merges)
-		die("--combined-all-paths makes no sense without -c or --cc");
+
+	diff_merges_setup_revs(revs);
 
 	revs->diffopt.abbrev = revs->abbrev;
 
@@ -3302,7 +3270,7 @@
 define_commit_slab(author_date_slab, timestamp_t);
 
 struct topo_walk_info {
-	uint32_t min_generation;
+	timestamp_t min_generation;
 	struct prio_queue explore_queue;
 	struct prio_queue indegree_queue;
 	struct prio_queue topo_queue;
@@ -3310,6 +3278,26 @@
 	struct author_date_slab author_date;
 };
 
+static int topo_walk_atexit_registered;
+static unsigned int count_explore_walked;
+static unsigned int count_indegree_walked;
+static unsigned int count_topo_walked;
+
+static void trace2_topo_walk_statistics_atexit(void)
+{
+	struct json_writer jw = JSON_WRITER_INIT;
+
+	jw_object_begin(&jw, 0);
+	jw_object_intmax(&jw, "count_explore_walked", count_explore_walked);
+	jw_object_intmax(&jw, "count_indegree_walked", count_indegree_walked);
+	jw_object_intmax(&jw, "count_topo_walked", count_topo_walked);
+	jw_end(&jw);
+
+	trace2_data_json("topo_walk", the_repository, "statistics", &jw);
+
+	jw_release(&jw);
+}
+
 static inline void test_flag_and_insert(struct prio_queue *q, struct commit *c, int flag)
 {
 	if (c->object.flags & flag)
@@ -3331,6 +3319,8 @@
 	if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
 		return;
 
+	count_explore_walked++;
+
 	if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
 		record_author_date(&info->author_date, c);
 
@@ -3348,7 +3338,7 @@
 }
 
 static void explore_to_depth(struct rev_info *revs,
-			     uint32_t gen_cutoff)
+			     timestamp_t gen_cutoff)
 {
 	struct topo_walk_info *info = revs->topo_walk_info;
 	struct commit *c;
@@ -3369,12 +3359,17 @@
 	if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
 		return;
 
+	count_indegree_walked++;
+
 	explore_to_depth(revs, commit_graph_generation(c));
 
 	for (p = c->parents; p; p = p->next) {
 		struct commit *parent = p->item;
 		int *pi = indegree_slab_at(&info->indegree, parent);
 
+		if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
+			return;
+
 		if (*pi)
 			(*pi)++;
 		else
@@ -3388,7 +3383,7 @@
 }
 
 static void compute_indegrees_to_depth(struct rev_info *revs,
-				       uint32_t gen_cutoff)
+				       timestamp_t gen_cutoff)
 {
 	struct topo_walk_info *info = revs->topo_walk_info;
 	struct commit *c;
@@ -3446,7 +3441,7 @@
 	info->min_generation = GENERATION_NUMBER_INFINITY;
 	for (list = revs->commits; list; list = list->next) {
 		struct commit *c = list->item;
-		uint32_t generation;
+		timestamp_t generation;
 
 		if (repo_parse_commit_gently(revs->repo, c, 1))
 			continue;
@@ -3478,6 +3473,11 @@
 	 */
 	if (revs->sort_order == REV_SORT_IN_GRAPH_ORDER)
 		prio_queue_reverse(&info->topo_queue);
+
+	if (trace2_is_enabled() && !topo_walk_atexit_registered) {
+		atexit(trace2_topo_walk_statistics_atexit);
+		topo_walk_atexit_registered = 1;
+	}
 }
 
 static struct commit *next_topo_commit(struct rev_info *revs)
@@ -3504,10 +3504,12 @@
 			    oid_to_hex(&commit->object.oid));
 	}
 
+	count_topo_walked++;
+
 	for (p = commit->parents; p; p = p->next) {
 		struct commit *parent = p->item;
 		int *pi;
-		uint32_t generation;
+		timestamp_t generation;
 
 		if (parent->object.flags & UNINTERESTING)
 			continue;
diff --git a/revision.h b/revision.h
index 086ff10..e6be3c8 100644
--- a/revision.h
+++ b/revision.h
@@ -191,11 +191,16 @@
 			match_missing:1,
 			no_commit_id:1,
 			verbose_header:1,
+			always_show_header:1,
+			/* Diff-merge flags */
+			explicit_diff_merges: 1,
+			merges_need_diff: 1,
+			separate_merges: 1,
 			combine_merges:1,
 			combined_all_paths:1,
+			combined_imply_patch:1,
 			dense_combined_merges:1,
-			always_show_header:1;
-	int             ignore_merges:2;
+			first_parent_merges:1;
 
 	/* Format info */
 	int		show_notes;
diff --git a/run-command.c b/run-command.c
index 25fbab2..4e34623 100644
--- a/run-command.c
+++ b/run-command.c
@@ -551,8 +551,11 @@
 
 	while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
 		;	/* nothing */
-	if (in_signal)
-		return 0;
+	if (in_signal) {
+		if (WIFEXITED(status))
+			code = WEXITSTATUS(status);
+		return code;
+	}
 
 	if (waiting < 0) {
 		failed_errno = errno;
diff --git a/run-command.h b/run-command.h
index 6472b38..d08414a 100644
--- a/run-command.h
+++ b/run-command.h
@@ -126,8 +126,15 @@
 	 */
 	unsigned silent_exec_failure:1;
 
-	unsigned stdout_to_stderr:1;
+	/**
+	 * Run the command from argv[0] using a shell (but note that we may
+	 * still optimize out the shell call if the command contains no
+	 * metacharacters). Note that further arguments to the command in
+	 * argv[1], etc, do not need to be shell-quoted.
+	 */
 	unsigned use_shell:1;
+
+	unsigned stdout_to_stderr:1;
 	unsigned clean_on_exit:1;
 	unsigned wait_after_clean:1;
 	void (*clean_on_exit_handler)(struct child_process *process);
diff --git a/sequencer.c b/sequencer.c
index 8909a46..d2332d3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -679,9 +679,6 @@
 
 static struct object_id *get_cache_tree_oid(struct index_state *istate)
 {
-	if (!istate->cache_tree)
-		istate->cache_tree = cache_tree();
-
 	if (!cache_tree_fully_valid(istate->cache_tree))
 		if (cache_tree_update(istate, 0)) {
 			error(_("unable to update cache tree"));
@@ -943,6 +940,7 @@
 #define CLEANUP_MSG (1<<3)
 #define VERIFY_MSG  (1<<4)
 #define CREATE_ROOT_COMMIT (1<<5)
+#define VERBATIM_MSG (1<<6)
 
 static int run_command_silent_on_success(struct child_process *cmd)
 {
@@ -979,6 +977,9 @@
 {
 	struct child_process cmd = CHILD_PROCESS_INIT;
 
+	if ((flags & CLEANUP_MSG) && (flags & VERBATIM_MSG))
+		BUG("CLEANUP_MSG and VERBATIM_MSG are mutually exclusive");
+
 	cmd.git_cmd = 1;
 
 	if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
@@ -1012,6 +1013,8 @@
 		strvec_pushl(&cmd.args, "-C", "HEAD", NULL);
 	if ((flags & CLEANUP_MSG))
 		strvec_push(&cmd.args, "--cleanup=strip");
+	if ((flags & VERBATIM_MSG))
+		strvec_push(&cmd.args, "--cleanup=verbatim");
 	if ((flags & EDIT_MSG))
 		strvec_push(&cmd.args, "-e");
 	else if (!(flags & CLEANUP_MSG) &&
@@ -1380,6 +1383,9 @@
 	enum commit_msg_cleanup_mode cleanup;
 	int res = 0;
 
+	if ((flags & CLEANUP_MSG) && (flags & VERBATIM_MSG))
+		BUG("CLEANUP_MSG and VERBATIM_MSG are mutually exclusive");
+
 	if (parse_head(r, &current_head))
 		return -1;
 
@@ -1454,6 +1460,8 @@
 
 	if (flags & CLEANUP_MSG)
 		cleanup = COMMIT_MSG_CLEANUP_ALL;
+	else if (flags & VERBATIM_MSG)
+		cleanup = COMMIT_MSG_CLEANUP_NONE;
 	else if ((opts->signoff || opts->record_origin) &&
 		 !opts->explicit_cleanup)
 		cleanup = COMMIT_MSG_CLEANUP_SPACE;
@@ -2002,7 +2010,7 @@
 		if (!final_fixup)
 			msg_file = rebase_path_squash_msg();
 		else if (file_exists(rebase_path_fixup_msg())) {
-			flags |= CLEANUP_MSG;
+			flags |= VERBATIM_MSG;
 			msg_file = rebase_path_fixup_msg();
 		} else {
 			const char *dest = git_path_squash_msg(r);
diff --git a/serve.c b/serve.c
index eec2fe6..ac20c72 100644
--- a/serve.c
+++ b/serve.c
@@ -73,7 +73,7 @@
 
 static struct protocol_capability capabilities[] = {
 	{ "agent", agent_advertise, NULL },
-	{ "ls-refs", always_advertise, ls_refs },
+	{ "ls-refs", ls_refs_advertise, ls_refs },
 	{ "fetch", upload_pack_advertise, upload_pack_v2 },
 	{ "server-option", always_advertise, NULL },
 	{ "object-format", object_format_advertise, NULL },
diff --git a/shallow.c b/shallow.c
index 91b9e10..9ed18eb 100644
--- a/shallow.c
+++ b/shallow.c
@@ -41,7 +41,7 @@
 
 int unregister_shallow(const struct object_id *oid)
 {
-	int pos = commit_graft_pos(the_repository, oid->hash);
+	int pos = commit_graft_pos(the_repository, oid);
 	if (pos < 0)
 		return -1;
 	if (pos + 1 < the_repository->parsed_objects->grafts_nr)
diff --git a/shortlog.h b/shortlog.h
index 64be879..3f7e9aa 100644
--- a/shortlog.h
+++ b/shortlog.h
@@ -23,7 +23,6 @@
 	} groups;
 	struct string_list trailers;
 
-	char *common_repo_prefix;
 	int email;
 	struct string_list mailmap;
 	FILE *file;
diff --git a/submodule.c b/submodule.c
index b561445..9767ba9 100644
--- a/submodule.c
+++ b/submodule.c
@@ -420,6 +420,7 @@
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
 				  const char *arg)
 {
+	diffopt->flags.ignore_submodule_set = 1;
 	diffopt->flags.ignore_submodules = 0;
 	diffopt->flags.ignore_untracked_in_submodules = 0;
 	diffopt->flags.ignore_dirty_submodules = 0;
diff --git a/t/.gitattributes b/t/.gitattributes
index df05434..dafa17c 100644
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@ -1,13 +1,13 @@
 t[0-9][0-9][0-9][0-9]/* -whitespace
 /chainlint/*.expect eol=lf
-/diff-lib/* eol=lf
+/lib-diff/* eol=lf
 /t0110/url-* binary
 /t3206/* eol=lf
 /t3900/*.txt eol=lf
 /t3901/*.txt eol=lf
-/t4034/*/* eol=lf
 /t4013/* eol=lf
 /t4018/* eol=lf
+/t4034/*/* eol=lf
 /t4051/* eol=lf
 /t4100/* eol=lf
 /t4101/* eol=lf
diff --git a/t/README b/t/README
index c730a70..593d4a4 100644
--- a/t/README
+++ b/t/README
@@ -358,12 +358,6 @@
 refactor to deal with it. The "SYMLINKS" prerequisite is currently
 excluded as so much relies on it, but this might change in the future.
 
-GIT_TEST_GETTEXT_POISON=<boolean> turns all strings marked for
-translation into gibberish if true. Used for spotting those tests that
-need to be marked with a C_LOCALE_OUTPUT prerequisite when adding more
-strings for translation. See "Testing marked strings" in po/README for
-details.
-
 GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
 test suite. Accept any boolean values that are accepted by git-config.
 
@@ -393,6 +387,9 @@
 be written after every 'git commit' command, and overrides the
 'core.commitGraph' setting to true.
 
+GIT_TEST_COMMIT_GRAPH_NO_GDAT=<boolean>, when true, forces the
+commit-graph to be written without generation data chunk.
+
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
 commit-graph write to compute and write changed path Bloom filters for
 every 'git commit-graph write', as if the `--changed-paths` option was
@@ -439,6 +436,9 @@
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
+GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
+'pack.writeReverseIndex' setting.
+
 Naming Tests
 ------------
 
@@ -917,13 +917,13 @@
 
    Check whether a file has the length it is expected to.
 
- - test_path_is_file <path> [<diagnosis>]
-   test_path_is_dir <path> [<diagnosis>]
-   test_path_is_missing <path> [<diagnosis>]
+ - test_path_is_file <path>
+   test_path_is_dir <path>
+   test_path_is_missing <path>
 
    Check if the named path is a file, if the named path is a
    directory, or if the named path does not exist, respectively,
-   and fail otherwise, showing the <diagnosis> text.
+   and fail otherwise.
 
  - test_when_finished <script>
 
@@ -1104,18 +1104,6 @@
    Git was compiled with support for PCRE. Wrap any tests
    that use git-grep --perl-regexp or git-grep -P in these.
 
- - LIBPCRE1
-
-   Git was compiled with PCRE v1 support via
-   USE_LIBPCRE1=YesPlease. Wrap any PCRE using tests that for some
-   reason need v1 of the PCRE library instead of v2 in these.
-
- - LIBPCRE2
-
-   Git was compiled with PCRE v2 support via
-   USE_LIBPCRE2=YesPlease. Wrap any PCRE using tests that for some
-   reason need v2 of the PCRE library instead of v1 in these.
-
  - CASE_INSENSITIVE_FS
 
    Test is run on a case insensitive file system.
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index a2e4657..29ce890 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -95,7 +95,7 @@
 '
 
 test_expect_success 'setup B1 lines (branch1)' '
-	git checkout -b branch1 master &&
+	git checkout -b branch1 main &&
 	echo "3A slow green fox jumps into the" >>file &&
 	echo "well." >>file &&
 	GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \
@@ -107,7 +107,7 @@
 '
 
 test_expect_success 'setup B2 lines (branch2)' '
-	git checkout -b branch2 master &&
+	git checkout -b branch2 main &&
 	sed -e "s/2A quick brown/4A quick brown lazy dog/" <file >file.new &&
 	mv file.new file &&
 	GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \
@@ -131,11 +131,11 @@
 '
 
 test_expect_success 'blame ancestor' '
-	check_count -h master A 2 B 2
+	check_count -h main A 2 B 2
 '
 
 test_expect_success 'blame great-ancestor' '
-	check_count -h master^ A 2
+	check_count -h main^ A 2
 '
 
 test_expect_success 'setup evil merge' '
diff --git a/t/helper/test-pcre2-config.c b/t/helper/test-pcre2-config.c
new file mode 100644
index 0000000..5258fdd
--- /dev/null
+++ b/t/helper/test-pcre2-config.c
@@ -0,0 +1,12 @@
+#include "test-tool.h"
+#include "cache.h"
+#include "grep.h"
+
+int cmd__pcre2_config(int argc, const char **argv)
+{
+	if (argc == 2 && !strcmp(argv[1], "has-PCRE2_MATCH_INVALID_UTF")) {
+		int value = PCRE2_MATCH_INVALID_UTF;
+		return !value;
+	}
+	return 1;
+}
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 5f585a1..75927b2 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -33,6 +33,10 @@
 		printf(" oid_lookup");
 	if (graph->chunk_commit_data)
 		printf(" commit_metadata");
+	if (graph->chunk_generation_data)
+		printf(" generation_data");
+	if (graph->chunk_generation_data_overflow)
+		printf(" generation_data_overflow");
 	if (graph->chunk_extra_edges)
 		printf(" extra_edges");
 	if (graph->chunk_bloom_indexes)
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 759e69d..bba5f84 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -72,18 +72,6 @@
 	return refs_pack_refs(refs, flags);
 }
 
-static int cmd_peel_ref(struct ref_store *refs, const char **argv)
-{
-	const char *refname = notnull(*argv++, "refname");
-	struct object_id oid;
-	int ret;
-
-	ret = refs_peel_ref(refs, refname, &oid);
-	if (!ret)
-		puts(oid_to_hex(&oid));
-	return ret;
-}
-
 static int cmd_create_symref(struct ref_store *refs, const char **argv)
 {
 	const char *refname = notnull(*argv++, "refname");
@@ -255,7 +243,6 @@
 
 static struct command commands[] = {
 	{ "pack-refs", cmd_pack_refs },
-	{ "peel-ref", cmd_peel_ref },
 	{ "create-symref", cmd_create_symref },
 	{ "delete-refs", cmd_delete_refs },
 	{ "rename-ref", cmd_rename_ref },
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 9d6d14d..f97cd9f 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -46,6 +46,7 @@
 	{ "parse-options", cmd__parse_options },
 	{ "parse-pathspec-file", cmd__parse_pathspec_file },
 	{ "path-utils", cmd__path_utils },
+	{ "pcre2-config", cmd__pcre2_config },
 	{ "pkt-line", cmd__pkt_line },
 	{ "prio-queue", cmd__prio_queue },
 	{ "proc-receive", cmd__proc_receive},
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index a6470ff..28072c0 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -35,6 +35,7 @@
 int cmd__parse_options(int argc, const char **argv);
 int cmd__parse_pathspec_file(int argc, const char** argv);
 int cmd__path_utils(int argc, const char **argv);
+int cmd__pcre2_config(int argc, const char **argv);
 int cmd__pkt_line(int argc, const char **argv);
 int cmd__prio_queue(int argc, const char **argv);
 int cmd__proc_receive(int argc, const char **argv);
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 823f33c..f93633f 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -198,6 +198,14 @@
 	return 0;
 }
 
+static int ut_007bug(int argc, const char **argv)
+{
+	/*
+	 * Exercise BUG() to ensure that the message is printed to trace2.
+	 */
+	BUG("the bug message");
+}
+
 /*
  * Usage:
  *     test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -214,6 +222,7 @@
 	{ ut_004child,    "004child",  "[<child_command_line>]" },
 	{ ut_005exec,     "005exec",   "<git_command_args>" },
 	{ ut_006data,     "006data",   "[<category> <key> <value>]+" },
+	{ ut_007bug,      "007bug",    "" },
 };
 /* clang-format on */
 
diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh
new file mode 100644
index 0000000..fe3f98b
--- /dev/null
+++ b/t/lib-bitmap.sh
@@ -0,0 +1,26 @@
+# Compare a file containing rev-list bitmap traversal output to its non-bitmap
+# counterpart. You can't just use test_cmp for this, because the two produce
+# subtly different output:
+#
+#   - regular output is in traversal order, whereas bitmap is split by type,
+#     with non-packed objects at the end
+#
+#   - regular output has a space and the pathname appended to non-commit
+#     objects; bitmap output omits this
+#
+# This function normalizes and compares the two. The second file should
+# always be the bitmap output.
+test_bitmap_traversal () {
+	if test "$1" = "--no-confirm-bitmaps"
+	then
+		shift
+	elif cmp "$1" "$2"
+	then
+		echo >&2 "identical raw outputs; are you sure bitmaps were used?"
+		return 1
+	fi &&
+	cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
+	sort "$2" >"$2.normalized" &&
+	test_cmp "$1.normalized" "$2.normalized" &&
+	rm -f "$1.normalized" "$2.normalized"
+}
diff --git a/t/lib-bundle.sh b/t/lib-bundle.sh
new file mode 100644
index 0000000..cf7ed81
--- /dev/null
+++ b/t/lib-bundle.sh
@@ -0,0 +1,42 @@
+# Library of git-bundle related functions.
+
+# Display the pack data contained in the bundle file, bypassing the
+# header that contains the signature, prerequisites and references.
+convert_bundle_to_pack () {
+	while read x && test -n "$x"
+	do
+		:;
+	done
+	cat
+}
+
+# Check count of objects in a bundle file.
+# We can use "--thin" opiton to check thin pack, which must be fixed by
+# command `git-index-pack --fix-thin --stdin`.
+test_bundle_object_count () {
+	thin=
+	if test "$1" = "--thin"
+	then
+		thin=t
+		shift
+	fi
+	if test $# -ne 2
+	then
+		echo >&2 "args should be: <bundle> <count>"
+		return 1
+	fi
+	bundle=$1
+	pack=$bundle.pack
+	convert_bundle_to_pack <"$bundle" >"$pack" &&
+	if test -n "$thin"
+	then
+		mv "$pack" "$bundle.thin.pack" &&
+		git index-pack --stdin --fix-thin "$pack" <"$bundle.thin.pack"
+	else
+		git index-pack "$pack"
+	fi || return 1
+	count=$(git show-index <"${pack%pack}idx" | wc -l) &&
+	test $2 = $count && return 0
+	echo >&2 "error: object count for $bundle is $count, not $2"
+	return 1
+}
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index dea2cbe..5ea8bc9 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -20,7 +20,7 @@
 		false
 	fi &&
 	test_cmp expect-stdout stdout &&
-	test_i18ncmp expect-stderr stderr
+	test_cmp expect-stderr stderr
 }
 
 read_chunk() {
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
index 9b2bcfb..32b3473 100644
--- a/t/lib-cvs.sh
+++ b/t/lib-cvs.sh
@@ -36,7 +36,7 @@
 test_cvs_co () {
 	# Usage: test_cvs_co BRANCH_NAME
 	rm -rf module-cvs-"$1"
-	if [ "$1" = "master" ]
+	if [ "$1" = "main" ]
 	then
 		$CVS co -P -d module-cvs-"$1" -A module
 	else
diff --git a/t/diff-lib.sh b/t/lib-diff.sh
similarity index 100%
rename from t/diff-lib.sh
rename to t/lib-diff.sh
diff --git a/t/diff-lib/COPYING b/t/lib-diff/COPYING
similarity index 100%
rename from t/diff-lib/COPYING
rename to t/lib-diff/COPYING
diff --git a/t/diff-lib/README b/t/lib-diff/README
similarity index 100%
rename from t/diff-lib/README
rename to t/lib-diff/README
diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh
index 2139b42..cc6bb2c 100644
--- a/t/lib-gettext.sh
+++ b/t/lib-gettext.sh
@@ -17,7 +17,7 @@
 	. "$GIT_BUILD_DIR"/git-sh-i18n
 fi
 
-if test_have_prereq GETTEXT && test_have_prereq C_LOCALE_OUTPUT
+if test_have_prereq GETTEXT
 then
 	# is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian
 	is_IS_locale=$(locale -a 2>/dev/null |
diff --git a/t/gitweb-lib.sh b/t/lib-gitweb.sh
similarity index 100%
rename from t/gitweb-lib.sh
rename to t/lib-gitweb.sh
diff --git a/t/lib-log-graph.sh b/t/lib-log-graph.sh
index 1184cce..bf952ef 100644
--- a/t/lib-log-graph.sh
+++ b/t/lib-log-graph.sh
@@ -12,13 +12,13 @@
 lib_test_cmp_graph () {
 	git log --graph "$@" >output &&
 	sed 's/ *$//' >output.sanitized <output &&
-	test_i18ncmp expect output.sanitized
+	test_cmp expect output.sanitized
 }
 
 lib_test_cmp_short_graph () {
 	git log --graph --pretty=short "$@" >output &&
 	sanitize_log_output >output.sanitized <output &&
-	test_i18ncmp expect output.sanitized
+	test_cmp expect output.sanitized
 }
 
 lib_test_cmp_colored_graph () {
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index b72c051..172d745 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -29,7 +29,6 @@
 	*/COMMIT_EDITMSG)
 		test -z "$EXPECT_HEADER_COUNT" ||
 			test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
-			test "# # GETTEXT POISON #" = "$(sed -n '1p' < "$1")" ||
 			exit
 		test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
 		test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
diff --git a/t/oid-info/README b/t/oid-info/README
index 27f843f..ca56a74 100644
--- a/t/oid-info/README
+++ b/t/oid-info/README
@@ -5,7 +5,7 @@
 (specifically, those whitespace in the default `$IFS`).  The key consists only
 of shell identifier characters, and the value consists of a hash algorithm,
 colon, and value.  The hash algorithm also consists only of shell identifier
-characters; it should match the value in sha1-file.c.
+characters; it should match the value in object-file.c.
 
 For example, the following lines map the key "rawsz" to "20" if SHA-1 is in use
 and to "32" if SHA-256 is in use:
diff --git a/t/perf/.gitignore b/t/perf/.gitignore
index 982eb8e..72f5d0d 100644
--- a/t/perf/.gitignore
+++ b/t/perf/.gitignore
@@ -1,3 +1,4 @@
 /build/
 /test-results/
+/test-trace/
 /trash directory*/
diff --git a/t/perf/Makefile b/t/perf/Makefile
index fcb0e88..2465770 100644
--- a/t/perf/Makefile
+++ b/t/perf/Makefile
@@ -7,10 +7,10 @@
 	./run
 
 pre-clean:
-	rm -rf test-results
+	rm -rf test-results test-trace
 
 clean:
-	rm -rf build "trash directory".* test-results
+	rm -rf build "trash directory".* test-results test-trace
 
 test-lint:
 	$(MAKE) -C .. test-lint
diff --git a/t/perf/p4205-log-pretty-formats.sh b/t/perf/p4205-log-pretty-formats.sh
index 7c26f4f..609fecd 100755
--- a/t/perf/p4205-log-pretty-formats.sh
+++ b/t/perf/p4205-log-pretty-formats.sh
@@ -6,7 +6,7 @@
 
 test_perf_default_repo
 
-for format in %H %h %T %t %P %p %h-%h-%h
+for format in %H %h %T %t %P %p %h-%h-%h %an-%ae-%s
 do
 	test_perf "log with $format" "
 		git log --format=\"$format\" >/dev/null
diff --git a/t/perf/p7519-fsmonitor.sh b/t/perf/p7519-fsmonitor.sh
index 1e20a18..b657564 100755
--- a/t/perf/p7519-fsmonitor.sh
+++ b/t/perf/p7519-fsmonitor.sh
@@ -32,6 +32,8 @@
 #
 # GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
 #
+# GIT_PERF_7519_TRACE: if set, enable trace logging during the test.
+#   Trace logs will be grouped by fsmonitor provider.
 
 test_perf_large_repo
 test_checkout_worktree
@@ -70,6 +72,32 @@
 	fi
 fi
 
+trace_start() {
+	if test -n "$GIT_PERF_7519_TRACE"
+	then
+		name="$1"
+		TEST_TRACE_DIR="$TEST_OUTPUT_DIRECTORY/test-trace/p7519/"
+		echo "Writing trace logging to $TEST_TRACE_DIR"
+
+		mkdir -p "$TEST_TRACE_DIR"
+
+		# Start Trace2 logging and any other GIT_TRACE_* logs that you
+		# want for this named test case.
+
+		GIT_TRACE2_PERF="$TEST_TRACE_DIR/$name.trace2perf"
+		export GIT_TRACE2_PERF
+
+		>"$GIT_TRACE2_PERF"
+	fi
+}
+
+trace_stop() {
+	if test -n "$GIT_PERF_7519_TRACE"
+	then
+		unset GIT_TRACE2_PERF
+	fi
+}
+
 test_expect_success "one time repo setup" '
 	# set untrackedCache depending on the environment
 	if test -n "$GIT_PERF_7519_UNTRACKED_CACHE"
@@ -101,7 +129,7 @@
 	# If Watchman exists, watch the work tree and attempt a query.
 	if test_have_prereq WATCHMAN; then
 		watchman watch "$GIT_WORK_TREE" &&
-		watchman watch-list | grep -q -F "$GIT_WORK_TREE"
+		watchman watch-list | grep -q -F "p7519-fsmonitor"
 	fi
 '
 
@@ -169,8 +197,18 @@
 		git status -uall
 	'
 
+	# Update the mtimes on upto 100k files to make status think
+	# that they are dirty.  For simplicity, omit any files with
+	# LFs (i.e. anything that ls-files thinks it needs to dquote).
+	# Then fully backslash-quote the paths to capture any
+	# whitespace so that they pass thru xargs properly.
+	#
 	test_perf_w_drop_caches "status (dirty) ($DESC)" '
-		git ls-files | head -100000 | xargs -d "\n" touch -h &&
+		git ls-files | \
+			head -100000 | \
+			grep -v \" | \
+			sed '\''s/\(.\)/\\\1/g'\'' | \
+			xargs test-tool chmtime -300 &&
 		git status
 	'
 
@@ -203,6 +241,12 @@
 	'
 }
 
+#
+# Run a full set of perf tests using each Hook-based fsmonitor provider,
+# such as Watchman.
+#
+
+trace_start fsmonitor-watchman
 if test -n "$GIT_PERF_7519_FSMONITOR"; then
 	for INTEGRATION_PATH in $GIT_PERF_7519_FSMONITOR; do
 		test_expect_success "setup for fsmonitor $INTEGRATION_PATH" 'setup_for_fsmonitor'
@@ -213,14 +257,6 @@
 	test_fsmonitor_suite
 fi
 
-test_expect_success "setup without fsmonitor" '
-	unset INTEGRATION_SCRIPT &&
-	git config --unset core.fsmonitor &&
-	git update-index --no-fsmonitor
-'
-
-test_fsmonitor_suite
-
 if test_have_prereq WATCHMAN
 then
 	watchman watch-del "$GIT_WORK_TREE" >/dev/null 2>&1 &&
@@ -229,5 +265,20 @@
 	# preventing the removal of the trash directory
 	watchman shutdown-server >/dev/null 2>&1
 fi
+trace_stop
+
+#
+# Run a full set of perf tests with the fsmonitor feature disabled.
+#
+
+trace_start fsmonitor-disabled
+test_expect_success "setup without fsmonitor" '
+	unset INTEGRATION_SCRIPT &&
+	git config --unset core.fsmonitor &&
+	git update-index --no-fsmonitor
+'
+
+test_fsmonitor_suite
+trace_stop
 
 test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index 22d727c..e385c68 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -147,10 +147,7 @@
 	"$GTIME" -f "%E %U %S" -o test_time.$i "$SHELL" -c '
 . '"$TEST_DIRECTORY"/test-lib-functions.sh'
 test_export () {
-	[ $# != 0 ] || return 0
-	test_export_="$test_export_ $1"
-	shift
-	test_export "$@"
+	test_export_="$test_export_ $*"
 }
 '"$1"'
 ret=$?
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index f4ba2e8..705d62c 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -135,32 +135,32 @@
 	)
 }
 
-test_expect_success 'pretend we have a fully passing test suite' "
-	run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF &&
+test_expect_success 'pretend we have a fully passing test suite' '
+	run_sub_test_lib_test full-pass "3 passing tests" <<-\EOF &&
 	for i in 1 2 3
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test full-pass <<-\\EOF
+	check_sub_test_lib_test full-pass <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 - passing test #3
 	> # passed all 3 test(s)
 	> 1..3
 	EOF
-"
+'
 
-test_expect_success 'pretend we have a partially passing test suite' "
+test_expect_success 'pretend we have a partially passing test suite' '
 	run_sub_test_lib_test_err \
-		partial-pass '2/3 tests passing' <<-\\EOF &&
-	test_expect_success 'passing test #1' 'true'
-	test_expect_success 'failing test #2' 'false'
-	test_expect_success 'passing test #3' 'true'
+		partial-pass "2/3 tests passing" <<-\EOF &&
+	test_expect_success "passing test #1" "true"
+	test_expect_success "failing test #2" "false"
+	test_expect_success "passing test #3" "true"
 	test_done
 	EOF
-	check_sub_test_lib_test partial-pass <<-\\EOF
+	check_sub_test_lib_test partial-pass <<-\EOF
 	> ok 1 - passing test #1
 	> not ok 2 - failing test #2
 	#	false
@@ -168,44 +168,44 @@
 	> # failed 1 among 3 test(s)
 	> 1..3
 	EOF
-"
+'
 
-test_expect_success 'pretend we have a known breakage' "
-	run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF &&
-	test_expect_success 'passing test' 'true'
-	test_expect_failure 'pretend we have a known breakage' 'false'
+test_expect_success 'pretend we have a known breakage' '
+	run_sub_test_lib_test failing-todo "A failing TODO test" <<-\EOF &&
+	test_expect_success "passing test" "true"
+	test_expect_failure "pretend we have a known breakage" "false"
 	test_done
 	EOF
-	check_sub_test_lib_test failing-todo <<-\\EOF
+	check_sub_test_lib_test failing-todo <<-\EOF
 	> ok 1 - passing test
 	> not ok 2 - pretend we have a known breakage # TODO known breakage
 	> # still have 1 known breakage(s)
 	> # passed all remaining 1 test(s)
 	> 1..2
 	EOF
-"
+'
 
-test_expect_success 'pretend we have fixed a known breakage' "
-	run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF &&
-	test_expect_failure 'pretend we have fixed a known breakage' 'true'
+test_expect_success 'pretend we have fixed a known breakage' '
+	run_sub_test_lib_test passing-todo "A passing TODO test" <<-\EOF &&
+	test_expect_failure "pretend we have fixed a known breakage" "true"
 	test_done
 	EOF
-	check_sub_test_lib_test passing-todo <<-\\EOF
+	check_sub_test_lib_test passing-todo <<-\EOF
 	> ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished
 	> # 1 known breakage(s) vanished; please update test(s)
 	> 1..1
 	EOF
-"
+'
 
-test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' "
+test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' '
 	run_sub_test_lib_test partially-passing-todos \
-		'2 TODO tests, one passing' <<-\\EOF &&
-	test_expect_failure 'pretend we have a known breakage' 'false'
-	test_expect_success 'pretend we have a passing test' 'true'
-	test_expect_failure 'pretend we have fixed another known breakage' 'true'
+		"2 TODO tests, one passing" <<-\EOF &&
+	test_expect_failure "pretend we have a known breakage" "false"
+	test_expect_success "pretend we have a passing test" "true"
+	test_expect_failure "pretend we have fixed another known breakage" "true"
 	test_done
 	EOF
-	check_sub_test_lib_test partially-passing-todos <<-\\EOF
+	check_sub_test_lib_test partially-passing-todos <<-\EOF
 	> not ok 1 - pretend we have a known breakage # TODO known breakage
 	> ok 2 - pretend we have a passing test
 	> ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished
@@ -214,17 +214,17 @@
 	> # passed all remaining 1 test(s)
 	> 1..3
 	EOF
-"
+'
 
-test_expect_success 'pretend we have a pass, fail, and known breakage' "
+test_expect_success 'pretend we have a pass, fail, and known breakage' '
 	run_sub_test_lib_test_err \
-		mixed-results1 'mixed results #1' <<-\\EOF &&
-	test_expect_success 'passing test' 'true'
-	test_expect_success 'failing test' 'false'
-	test_expect_failure 'pretend we have a known breakage' 'false'
+		mixed-results1 "mixed results #1" <<-\EOF &&
+	test_expect_success "passing test" "true"
+	test_expect_success "failing test" "false"
+	test_expect_failure "pretend we have a known breakage" "false"
 	test_done
 	EOF
-	check_sub_test_lib_test mixed-results1 <<-\\EOF
+	check_sub_test_lib_test mixed-results1 <<-\EOF
 	> ok 1 - passing test
 	> not ok 2 - failing test
 	> #	false
@@ -233,24 +233,24 @@
 	> # failed 1 among remaining 2 test(s)
 	> 1..3
 	EOF
-"
+'
 
-test_expect_success 'pretend we have a mix of all possible results' "
+test_expect_success 'pretend we have a mix of all possible results' '
 	run_sub_test_lib_test_err \
-		mixed-results2 'mixed results #2' <<-\\EOF &&
-	test_expect_success 'passing test' 'true'
-	test_expect_success 'passing test' 'true'
-	test_expect_success 'passing test' 'true'
-	test_expect_success 'passing test' 'true'
-	test_expect_success 'failing test' 'false'
-	test_expect_success 'failing test' 'false'
-	test_expect_success 'failing test' 'false'
-	test_expect_failure 'pretend we have a known breakage' 'false'
-	test_expect_failure 'pretend we have a known breakage' 'false'
-	test_expect_failure 'pretend we have fixed a known breakage' 'true'
+		mixed-results2 "mixed results #2" <<-\EOF &&
+	test_expect_success "passing test" "true"
+	test_expect_success "passing test" "true"
+	test_expect_success "passing test" "true"
+	test_expect_success "passing test" "true"
+	test_expect_success "failing test" "false"
+	test_expect_success "failing test" "false"
+	test_expect_success "failing test" "false"
+	test_expect_failure "pretend we have a known breakage" "false"
+	test_expect_failure "pretend we have a known breakage" "false"
+	test_expect_failure "pretend we have fixed a known breakage" "true"
 	test_done
 	EOF
-	check_sub_test_lib_test mixed-results2 <<-\\EOF
+	check_sub_test_lib_test mixed-results2 <<-\EOF
 	> ok 1 - passing test
 	> ok 2 - passing test
 	> ok 3 - passing test
@@ -269,9 +269,9 @@
 	> # failed 3 among remaining 7 test(s)
 	> 1..10
 	EOF
-"
+'
 
-test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
+test_expect_success 'test --verbose' '
 	run_sub_test_lib_test_err \
 		t1234-verbose "test verbose" --verbose <<-\EOF &&
 	test_expect_success "passing test" true
@@ -321,18 +321,18 @@
 	EOF
 '
 
-test_expect_success 'GIT_SKIP_TESTS' "
+test_expect_success 'GIT_SKIP_TESTS' '
 	(
-		GIT_SKIP_TESTS='git.2' && export GIT_SKIP_TESTS &&
+		GIT_SKIP_TESTS="git.2" && export GIT_SKIP_TESTS &&
 		run_sub_test_lib_test git-skip-tests-basic \
-			'GIT_SKIP_TESTS' <<-\\EOF &&
+			"GIT_SKIP_TESTS" <<-\EOF &&
 		for i in 1 2 3
 		do
-			test_expect_success \"passing test #\$i\" 'true'
+			test_expect_success "passing test #$i" "true"
 		done
 		test_done
 		EOF
-		check_sub_test_lib_test git-skip-tests-basic <<-\\EOF
+		check_sub_test_lib_test git-skip-tests-basic <<-\EOF
 		> ok 1 - passing test #1
 		> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
 		> ok 3 - passing test #3
@@ -340,20 +340,20 @@
 		> 1..3
 		EOF
 	)
-"
+'
 
-test_expect_success 'GIT_SKIP_TESTS several tests' "
+test_expect_success 'GIT_SKIP_TESTS several tests' '
 	(
-		GIT_SKIP_TESTS='git.2 git.5' && export GIT_SKIP_TESTS &&
+		GIT_SKIP_TESTS="git.2 git.5" && export GIT_SKIP_TESTS &&
 		run_sub_test_lib_test git-skip-tests-several \
-			'GIT_SKIP_TESTS several tests' <<-\\EOF &&
+			"GIT_SKIP_TESTS several tests" <<-\EOF &&
 		for i in 1 2 3 4 5 6
 		do
-			test_expect_success \"passing test #\$i\" 'true'
+			test_expect_success "passing test #$i" "true"
 		done
 		test_done
 		EOF
-		check_sub_test_lib_test git-skip-tests-several <<-\\EOF
+		check_sub_test_lib_test git-skip-tests-several <<-\EOF
 		> ok 1 - passing test #1
 		> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
 		> ok 3 - passing test #3
@@ -364,20 +364,20 @@
 		> 1..6
 		EOF
 	)
-"
+'
 
-test_expect_success 'GIT_SKIP_TESTS sh pattern' "
+test_expect_success 'GIT_SKIP_TESTS sh pattern' '
 	(
-		GIT_SKIP_TESTS='git.[2-5]' && export GIT_SKIP_TESTS &&
+		GIT_SKIP_TESTS="git.[2-5]" && export GIT_SKIP_TESTS &&
 		run_sub_test_lib_test git-skip-tests-sh-pattern \
-			'GIT_SKIP_TESTS sh pattern' <<-\\EOF &&
+			"GIT_SKIP_TESTS sh pattern" <<-\EOF &&
 		for i in 1 2 3 4 5 6
 		do
-			test_expect_success \"passing test #\$i\" 'true'
+			test_expect_success "passing test #$i" "true"
 		done
 		test_done
 		EOF
-		check_sub_test_lib_test git-skip-tests-sh-pattern <<-\\EOF
+		check_sub_test_lib_test git-skip-tests-sh-pattern <<-\EOF
 		> ok 1 - passing test #1
 		> ok 2 # skip passing test #2 (GIT_SKIP_TESTS)
 		> ok 3 # skip passing test #3 (GIT_SKIP_TESTS)
@@ -388,37 +388,37 @@
 		> 1..6
 		EOF
 	)
-"
+'
 
-test_expect_success 'GIT_SKIP_TESTS entire suite' "
+test_expect_success 'GIT_SKIP_TESTS entire suite' '
 	(
-		GIT_SKIP_TESTS='git' && export GIT_SKIP_TESTS &&
+		GIT_SKIP_TESTS="git" && export GIT_SKIP_TESTS &&
 		run_sub_test_lib_test git-skip-tests-entire-suite \
-			'GIT_SKIP_TESTS entire suite' <<-\\EOF &&
+			"GIT_SKIP_TESTS entire suite" <<-\EOF &&
 		for i in 1 2 3
 		do
-			test_expect_success \"passing test #\$i\" 'true'
+			test_expect_success "passing test #$i" "true"
 		done
 		test_done
 		EOF
-		check_sub_test_lib_test git-skip-tests-entire-suite <<-\\EOF
+		check_sub_test_lib_test git-skip-tests-entire-suite <<-\EOF
 		> 1..0 # SKIP skip all tests in git
 		EOF
 	)
-"
+'
 
-test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' "
+test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' '
 	(
-		GIT_SKIP_TESTS='notgit' && export GIT_SKIP_TESTS &&
+		GIT_SKIP_TESTS="notgit" && export GIT_SKIP_TESTS &&
 		run_sub_test_lib_test git-skip-tests-unmatched-suite \
-			'GIT_SKIP_TESTS does not skip unmatched suite' <<-\\EOF &&
+			"GIT_SKIP_TESTS does not skip unmatched suite" <<-\EOF &&
 		for i in 1 2 3
 		do
-			test_expect_success \"passing test #\$i\" 'true'
+			test_expect_success "passing test #$i" "true"
 		done
 		test_done
 		EOF
-		check_sub_test_lib_test git-skip-tests-unmatched-suite <<-\\EOF
+		check_sub_test_lib_test git-skip-tests-unmatched-suite <<-\EOF
 		> ok 1 - passing test #1
 		> ok 2 - passing test #2
 		> ok 3 - passing test #3
@@ -426,18 +426,18 @@
 		> 1..3
 		EOF
 	)
-"
+'
 
-test_expect_success '--run basic' "
+test_expect_success '--run basic' '
 	run_sub_test_lib_test run-basic \
-		'--run basic' --run='1,3,5' <<-\\EOF &&
+		"--run basic" --run="1,3,5" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-basic <<-\\EOF
+	check_sub_test_lib_test run-basic <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 # skip passing test #2 (--run)
 	> ok 3 - passing test #3
@@ -447,18 +447,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with a range' "
+test_expect_success '--run with a range' '
 	run_sub_test_lib_test run-range \
-		'--run with a range' --run='1-3' <<-\\EOF &&
+		"--run with a range" --run="1-3" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-range <<-\\EOF
+	check_sub_test_lib_test run-range <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 - passing test #3
@@ -468,18 +468,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with two ranges' "
+test_expect_success '--run with two ranges' '
 	run_sub_test_lib_test run-two-ranges \
-		'--run with two ranges' --run='1-2,5-6' <<-\\EOF &&
+		"--run with two ranges" --run="1-2,5-6" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-two-ranges <<-\\EOF
+	check_sub_test_lib_test run-two-ranges <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -489,18 +489,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with a left open range' "
+test_expect_success '--run with a left open range' '
 	run_sub_test_lib_test run-left-open-range \
-		'--run with a left open range' --run='-3' <<-\\EOF &&
+		"--run with a left open range" --run="-3" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-left-open-range <<-\\EOF
+	check_sub_test_lib_test run-left-open-range <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 - passing test #3
@@ -510,18 +510,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with a right open range' "
+test_expect_success '--run with a right open range' '
 	run_sub_test_lib_test run-right-open-range \
-		'--run with a right open range' --run='4-' <<-\\EOF &&
+		"--run with a right open range" --run="4-" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-right-open-range <<-\\EOF
+	check_sub_test_lib_test run-right-open-range <<-\EOF
 	> ok 1 # skip passing test #1 (--run)
 	> ok 2 # skip passing test #2 (--run)
 	> ok 3 # skip passing test #3 (--run)
@@ -531,18 +531,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with basic negation' "
+test_expect_success '--run with basic negation' '
 	run_sub_test_lib_test run-basic-neg \
-		'--run with basic negation' --run='"'!3'"' <<-\\EOF &&
+		"--run with basic negation" --run="!3" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-basic-neg <<-\\EOF
+	check_sub_test_lib_test run-basic-neg <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -552,18 +552,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run with two negations' "
+test_expect_success '--run with two negations' '
 	run_sub_test_lib_test run-two-neg \
-		'--run with two negations' --run='"'!3,!6'"' <<-\\EOF &&
+		"--run with two negations" --run="!3,!6" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-two-neg <<-\\EOF
+	check_sub_test_lib_test run-two-neg <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -573,18 +573,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run a range and negation' "
+test_expect_success '--run a range and negation' '
 	run_sub_test_lib_test run-range-and-neg \
-		'--run a range and negation' --run='"'-4,!2'"' <<-\\EOF &&
+		"--run a range and negation" --run="-4,!2" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-range-and-neg <<-\\EOF
+	check_sub_test_lib_test run-range-and-neg <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 # skip passing test #2 (--run)
 	> ok 3 - passing test #3
@@ -594,18 +594,18 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run range negation' "
+test_expect_success '--run range negation' '
 	run_sub_test_lib_test run-range-neg \
-		'--run range negation' --run='"'!1-3'"' <<-\\EOF &&
+		"--run range negation" --run="!1-3" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-range-neg <<-\\EOF
+	check_sub_test_lib_test run-range-neg <<-\EOF
 	> ok 1 # skip passing test #1 (--run)
 	> ok 2 # skip passing test #2 (--run)
 	> ok 3 # skip passing test #3 (--run)
@@ -615,19 +615,19 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run include, exclude and include' "
+test_expect_success '--run include, exclude and include' '
 	run_sub_test_lib_test run-inc-neg-inc \
-		'--run include, exclude and include' \
-		--run='"'1-5,!1-3,2'"' <<-\\EOF &&
+		"--run include, exclude and include" \
+		--run="1-5,!1-3,2" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-inc-neg-inc <<-\\EOF
+	check_sub_test_lib_test run-inc-neg-inc <<-\EOF
 	> ok 1 # skip passing test #1 (--run)
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -637,19 +637,19 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run include, exclude and include, comma separated' "
+test_expect_success '--run include, exclude and include, comma separated' '
 	run_sub_test_lib_test run-inc-neg-inc-comma \
-		'--run include, exclude and include, comma separated' \
-		--run=1-5,\!1-3,2 <<-\\EOF &&
+		"--run include, exclude and include, comma separated" \
+		--run=1-5,!1-3,2 <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-inc-neg-inc-comma <<-\\EOF
+	check_sub_test_lib_test run-inc-neg-inc-comma <<-\EOF
 	> ok 1 # skip passing test #1 (--run)
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -659,19 +659,19 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run exclude and include' "
+test_expect_success '--run exclude and include' '
 	run_sub_test_lib_test run-neg-inc \
-		'--run exclude and include' \
-		--run='"'!3-,5'"' <<-\\EOF &&
+		"--run exclude and include" \
+		--run="!3-,5" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-neg-inc <<-\\EOF
+	check_sub_test_lib_test run-neg-inc <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 - passing test #2
 	> ok 3 # skip passing test #3 (--run)
@@ -681,19 +681,19 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run empty selectors' "
+test_expect_success '--run empty selectors' '
 	run_sub_test_lib_test run-empty-sel \
-		'--run empty selectors' \
-		--run='1,,3,,,5' <<-\\EOF &&
+		"--run empty selectors" \
+		--run="1,,3,,,5" <<-\EOF &&
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"passing test #\$i\" 'true'
+		test_expect_success "passing test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-empty-sel <<-\\EOF
+	check_sub_test_lib_test run-empty-sel <<-\EOF
 	> ok 1 - passing test #1
 	> ok 2 # skip passing test #2 (--run)
 	> ok 3 - passing test #3
@@ -703,20 +703,20 @@
 	> # passed all 6 test(s)
 	> 1..6
 	EOF
-"
+'
 
-test_expect_success '--run substring selector' "
+test_expect_success '--run substring selector' '
 	run_sub_test_lib_test run-substring-selector \
-		'--run empty selectors' \
-		--run='relevant' <<-\\EOF &&
-	test_expect_success \"relevant test\" 'true'
+		"--run empty selectors" \
+		--run="relevant" <<-\EOF &&
+	test_expect_success "relevant test" "true"
 	for i in 1 2 3 4 5 6
 	do
-		test_expect_success \"other test #\$i\" 'true'
+		test_expect_success "other test #$i" "true"
 	done
 	test_done
 	EOF
-	check_sub_test_lib_test run-substring-selector <<-\\EOF
+	check_sub_test_lib_test run-substring-selector <<-\EOF
 	> ok 1 - relevant test
 	> ok 2 # skip other test #1 (--run)
 	> ok 3 # skip other test #2 (--run)
@@ -727,167 +727,165 @@
 	> # passed all 7 test(s)
 	> 1..7
 	EOF
-"
+'
 
-test_expect_success '--run keyword selection' "
+test_expect_success '--run keyword selection' '
 	run_sub_test_lib_test_err run-inv-range-start \
-		'--run invalid range start' \
-		--run='a-5' <<-\\EOF &&
-	test_expect_success \"passing test #1\" 'true'
+		"--run invalid range start" \
+		--run="a-5" <<-\EOF &&
+	test_expect_success "passing test #1" "true"
 	test_done
 	EOF
 	check_sub_test_lib_test_err run-inv-range-start \
-		<<-\\EOF_OUT 3<<-\\EOF_ERR
+		<<-\EOF_OUT 3<<-EOF_ERR
 	> FATAL: Unexpected exit with code 1
 	EOF_OUT
-	> error: --run: invalid non-numeric in range start: 'a-5'
+	> error: --run: invalid non-numeric in range start: ${SQ}a-5${SQ}
 	EOF_ERR
-"
+'
 
-test_expect_success '--run invalid range end' "
+test_expect_success '--run invalid range end' '
 	run_sub_test_lib_test_err run-inv-range-end \
-		'--run invalid range end' \
-		--run='1-z' <<-\\EOF &&
-	test_expect_success \"passing test #1\" 'true'
+		"--run invalid range end" \
+		--run="1-z" <<-\EOF &&
+	test_expect_success "passing test #1" "true"
 	test_done
 	EOF
 	check_sub_test_lib_test_err run-inv-range-end \
-		<<-\\EOF_OUT 3<<-\\EOF_ERR
+		<<-\EOF_OUT 3<<-EOF_ERR
 	> FATAL: Unexpected exit with code 1
 	EOF_OUT
-	> error: --run: invalid non-numeric in range end: '1-z'
+	> error: --run: invalid non-numeric in range end: ${SQ}1-z${SQ}
 	EOF_ERR
-"
-
-
-test_set_prereq HAVEIT
-haveit=no
-test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
-	test_have_prereq HAVEIT &&
-	haveit=yes
-'
-donthaveit=yes
-test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
-	donthaveit=no
-'
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $haveit$donthaveit != yesyes
-then
-	say "bug in test framework: prerequisite tags do not work reliably"
-	exit 1
-fi
-
-test_set_prereq HAVETHIS
-haveit=no
-test_expect_success HAVETHIS,HAVEIT 'test runs if prerequisites are satisfied' '
-	test_have_prereq HAVEIT &&
-	test_have_prereq HAVETHIS &&
-	haveit=yes
-'
-donthaveit=yes
-test_expect_success HAVEIT,DONTHAVEIT 'unmet prerequisites causes test to be skipped' '
-	donthaveit=no
-'
-donthaveiteither=yes
-test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' '
-	donthaveiteither=no
-'
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $haveit$donthaveit$donthaveiteither != yesyesyes
-then
-	say "bug in test framework: multiple prerequisite tags do not work reliably"
-	exit 1
-fi
-
-test_lazy_prereq LAZY_TRUE true
-havetrue=no
-test_expect_success LAZY_TRUE 'test runs if lazy prereq is satisfied' '
-	havetrue=yes
-'
-donthavetrue=yes
-test_expect_success !LAZY_TRUE 'missing lazy prereqs skip tests' '
-	donthavetrue=no
 '
 
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a "$havetrue$donthavetrue" != yesyes
-then
-	say 'bug in test framework: lazy prerequisites do not work'
-	exit 1
-fi
+test_expect_success 'tests respect prerequisites' '
+	run_sub_test_lib_test prereqs "tests respect prereqs" <<-\EOF &&
 
-test_lazy_prereq LAZY_FALSE false
-nothavefalse=no
-test_expect_success !LAZY_FALSE 'negative lazy prereqs checked' '
-	nothavefalse=yes
-'
-havefalse=yes
-test_expect_success LAZY_FALSE 'missing negative lazy prereqs will skip' '
-	havefalse=no
-'
+	test_set_prereq HAVEIT
+	test_expect_success HAVEIT "prereq is satisfied" "true"
+	test_expect_success "have_prereq works" "
+		test_have_prereq HAVEIT
+	"
+	test_expect_success DONTHAVEIT "prereq not satisfied" "false"
 
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a "$nothavefalse$havefalse" != yesyes
-then
-	say 'bug in test framework: negative lazy prerequisites do not work'
-	exit 1
-fi
-
-clean=no
-test_expect_success 'tests clean up after themselves' '
-	test_when_finished clean=yes
-'
-
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" -a $clean != yes
-then
-	say "bug in test framework: basic cleanup command does not work reliably"
-	exit 1
-fi
-
-test_lazy_prereq NESTED_INNER '
-	>inner &&
-	rm -f outer
-'
-test_lazy_prereq NESTED_PREREQ '
-	>outer &&
-	test_have_prereq NESTED_INNER &&
-	echo "can create new file in cwd" >file &&
-	test -f outer &&
-	test ! -f inner
-'
-test_expect_success NESTED_PREREQ 'evaluating nested lazy prereqs dont interfere with each other' '
-	nestedworks=yes
-'
-
-if test -z "$GIT_TEST_FAIL_PREREQS_INTERNAL" && test "$nestedworks" != yes
-then
-	say 'bug in test framework: nested lazy prerequisites do not work'
-	exit 1
-fi
-
-test_expect_success 'lazy prereqs do not turn off tracing' "
-	run_sub_test_lib_test lazy-prereq-and-tracing \
-		'lazy prereqs and -x' -v -x <<-\\EOF &&
-	test_lazy_prereq LAZY true
-
-	test_expect_success lazy 'test_have_prereq LAZY && echo trace'
+	test_set_prereq HAVETHIS
+	test_expect_success HAVETHIS,HAVEIT "multiple prereqs" "true"
+	test_expect_success HAVEIT,DONTHAVEIT "mixed prereqs (yes,no)" "false"
+	test_expect_success DONTHAVEIT,HAVEIT "mixed prereqs (no,yes)" "false"
 
 	test_done
 	EOF
 
-	grep 'echo trace' lazy-prereq-and-tracing/err
-"
+	check_sub_test_lib_test prereqs <<-\EOF
+	ok 1 - prereq is satisfied
+	ok 2 - have_prereq works
+	ok 3 # skip prereq not satisfied (missing DONTHAVEIT)
+	ok 4 - multiple prereqs
+	ok 5 # skip mixed prereqs (yes,no) (missing DONTHAVEIT of HAVEIT,DONTHAVEIT)
+	ok 6 # skip mixed prereqs (no,yes) (missing DONTHAVEIT of DONTHAVEIT,HAVEIT)
+	# passed all 6 test(s)
+	1..6
+	EOF
+'
 
-test_expect_success 'tests clean up even on failures' "
+test_expect_success 'tests respect lazy prerequisites' '
+	run_sub_test_lib_test lazy-prereqs "respect lazy prereqs" <<-\EOF &&
+
+	test_lazy_prereq LAZY_TRUE true
+	test_expect_success LAZY_TRUE "lazy prereq is satisifed" "true"
+	test_expect_success !LAZY_TRUE "negative lazy prereq" "false"
+
+	test_lazy_prereq LAZY_FALSE false
+	test_expect_success LAZY_FALSE "lazy prereq not satisfied" "false"
+	test_expect_success !LAZY_FALSE "negative false prereq" "true"
+
+	test_done
+	EOF
+
+	check_sub_test_lib_test lazy-prereqs <<-\EOF
+	ok 1 - lazy prereq is satisifed
+	ok 2 # skip negative lazy prereq (missing !LAZY_TRUE)
+	ok 3 # skip lazy prereq not satisfied (missing LAZY_FALSE)
+	ok 4 - negative false prereq
+	# passed all 4 test(s)
+	1..4
+	EOF
+'
+
+test_expect_success 'nested lazy prerequisites' '
+	run_sub_test_lib_test nested-lazy "nested lazy prereqs" <<-\EOF &&
+
+	test_lazy_prereq NESTED_INNER "
+		>inner &&
+		rm -f outer
+	"
+	test_lazy_prereq NESTED_PREREQ "
+		>outer &&
+		test_have_prereq NESTED_INNER &&
+		echo can create new file in cwd >file &&
+		test_path_is_file outer &&
+		test_path_is_missing inner
+	"
+	test_expect_success NESTED_PREREQ "evaluate nested prereq" "true"
+
+	test_done
+	EOF
+
+	check_sub_test_lib_test nested-lazy <<-\EOF
+	ok 1 - evaluate nested prereq
+	# passed all 1 test(s)
+	1..1
+	EOF
+'
+
+test_expect_success 'lazy prereqs do not turn off tracing' '
+	run_sub_test_lib_test lazy-prereq-and-tracing \
+		"lazy prereqs and -x" -v -x <<-\EOF &&
+	test_lazy_prereq LAZY true
+
+	test_expect_success lazy "test_have_prereq LAZY && echo trace"
+
+	test_done
+	EOF
+
+	grep "echo trace" lazy-prereq-and-tracing/err
+'
+
+test_expect_success 'tests clean up after themselves' '
+	run_sub_test_lib_test cleanup "test with cleanup" <<-\EOF &&
+	clean=no
+	test_expect_success "do cleanup" "
+		test_when_finished clean=yes
+	"
+	test_expect_success "cleanup happened" "
+		test $clean = yes
+	"
+	test_done
+	EOF
+
+	check_sub_test_lib_test cleanup <<-\EOF
+	ok 1 - do cleanup
+	ok 2 - cleanup happened
+	# passed all 2 test(s)
+	1..2
+	EOF
+'
+
+test_expect_success 'tests clean up even on failures' '
 	run_sub_test_lib_test_err \
-		failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF &&
-	test_expect_success 'tests clean up even after a failure' '
+		failing-cleanup "Failing tests with cleanup commands" <<-\EOF &&
+	test_expect_success "tests clean up even after a failure" "
 		touch clean-after-failure &&
 		test_when_finished rm clean-after-failure &&
 		(exit 1)
-	'
-	test_expect_success 'failure to clean up causes the test to fail' '
+	"
+	test_expect_success "failure to clean up causes the test to fail" "
 		test_when_finished \"(exit 2)\"
-	'
+	"
 	test_done
 	EOF
-	check_sub_test_lib_test failing-cleanup <<-\\EOF
+	check_sub_test_lib_test failing-cleanup <<-\EOF
 	> not ok 1 - tests clean up even after a failure
 	> #	Z
 	> #	touch clean-after-failure &&
@@ -896,30 +894,30 @@
 	> #	Z
 	> not ok 2 - failure to clean up causes the test to fail
 	> #	Z
-	> #	test_when_finished \"(exit 2)\"
+	> #	test_when_finished "(exit 2)"
 	> #	Z
 	> # failed 2 among 2 test(s)
 	> 1..2
 	EOF
-"
+'
 
-test_expect_success 'test_atexit is run' "
+test_expect_success 'test_atexit is run' '
 	run_sub_test_lib_test_err \
-		atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
-	test_expect_success 'tests clean up even after a failure' '
+		atexit-cleanup "Run atexit commands" -i <<-\EOF &&
+	test_expect_success "tests clean up even after a failure" "
 		> ../../clean-atexit &&
 		test_atexit rm ../../clean-atexit &&
 		> ../../also-clean-atexit &&
 		test_atexit rm ../../also-clean-atexit &&
 		> ../../dont-clean-atexit &&
 		(exit 1)
-	'
+	"
 	test_done
 	EOF
 	test_path_is_file dont-clean-atexit &&
 	test_path_is_missing clean-atexit &&
 	test_path_is_missing also-clean-atexit
-"
+'
 
 test_expect_success 'test_oid provides sane info by default' '
 	test_oid zero >actual &&
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index 960ed15..8440e6a 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -4,6 +4,9 @@
 
 Verify that plumbing commands work when .git is a file
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 objpath() {
@@ -96,7 +99,7 @@
 	git ls-remote enter_repo >actual &&
 	cat >expected <<-EOF &&
 	$head	HEAD
-	$head	refs/heads/master
+	$head	refs/heads/main
 	$head	refs/tags/foo
 	EOF
 	test_cmp expected actual
@@ -111,7 +114,7 @@
 	git ls-remote foo >actual &&
 	cat >expected <<-EOF &&
 	$head	HEAD
-	$head	refs/heads/master
+	$head	refs/heads/main
 	$head	refs/tags/foo
 	EOF
 	test_cmp expected actual
@@ -122,7 +125,7 @@
 	git ls-remote --upload-pack="git upload-pack --strict" foo/.git >actual &&
 	cat >expected <<-EOF &&
 	$head	HEAD
-	$head	refs/heads/master
+	$head	refs/heads/main
 	$head	refs/tags/foo
 	EOF
 	test_cmp expected actual
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 75ee9a9..6b757d7 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -11,7 +11,7 @@
 	echo "$t -> $2" >expect
 	test_expect_${3:-success} "relative date ($2)" "
 	test-tool date relative $t >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 	"
 }
 
@@ -139,7 +139,7 @@
 	echo "$t -> $2" >expect
 	test_expect_success "human date $t" '
 		test-tool date human $t >actual &&
-		test_i18ncmp expect actual
+		test_cmp expect actual
 '
 }
 
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index 370a389..f7abde6 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -34,7 +34,7 @@
 test_stderr () {
 	expected="$1"
 	expect_in stderr "$1" &&
-	test_i18ncmp "$HOME/expected-stderr" "$HOME/stderr"
+	test_cmp "$HOME/expected-stderr" "$HOME/stderr"
 }
 
 broken_c_unquote () {
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index e8ef730..5679e29 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -55,7 +55,7 @@
 		git: 'revisions' is not a git command. See 'git --help'.
 	EOF
 	test_must_fail git revisions --help 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 "
 
 test_expect_success 'git help' '
diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh
index c1ecf6a..4a159f9 100755
--- a/t/t0017-env-helper.sh
+++ b/t/t0017-env-helper.sh
@@ -86,14 +86,14 @@
 	git config -f home/cycle include.path .gitconfig &&
 
 	test_must_fail \
-		env HOME="$(pwd)/home" GIT_TEST_GETTEXT_POISON=false \
+		env HOME="$(pwd)/home" \
 		git config -l 2>err &&
 	grep "exceeded maximum include depth" err &&
 
 	test_must_fail \
-		env HOME="$(pwd)/home" GIT_TEST_GETTEXT_POISON=true \
-		git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_GETTEXT_POISON 2>err &&
-	grep "# GETTEXT POISON #" err
+		env HOME="$(pwd)/home" GIT_TEST_ENV_HELPER=true \
+		git -C cycle env--helper --type=bool --default=0 --exit-code GIT_TEST_ENV_HELPER 2>err &&
+	grep "exceeded maximum include depth" err
 '
 
 test_done
diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh
index e03554d..39e5e4b 100755
--- a/t/t0018-advice.sh
+++ b/t/t0018-advice.sh
@@ -10,7 +10,7 @@
 	hint: Disable this message with "git config advice.nestedTag false"
 	EOF
 	test-tool advise "This is a piece of advice" 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'advice should be printed when config variable is set to true' '
@@ -20,7 +20,7 @@
 	EOF
 	test_config advice.nestedTag true &&
 	test-tool advise "This is a piece of advice" 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'advice should not be printed when config variable is set to false' '
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index b63ba62..f25ae8b 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -2,6 +2,9 @@
 
 test_description='CRLF conversion'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 has_cr() {
@@ -84,10 +87,8 @@
 	git commit -m "nowarn" &&
 	for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
 	git add doublewarn 2>err &&
-	if test_have_prereq C_LOCALE_OUTPUT
-	then
-		test $(grep "CRLF will be replaced by LF" err | wc -l) = 1
-	fi
+	grep "CRLF will be replaced by LF" err >err.warnings &&
+	test_line_count = 1 err.warnings
 '
 
 
@@ -318,8 +319,8 @@
 	git add .gitattributes .file &&
 	git commit -m second &&
 
-	git checkout master~1 &&
-	git checkout master &&
+	git checkout main~1 &&
+	git checkout main &&
 	test "$(git diff-files --raw)" = ""
 
 '
@@ -331,8 +332,8 @@
 	git add .file2 &&
 	git commit -m third &&
 
-	git checkout master~1 &&
-	git checkout master &&
+	git checkout main~1 &&
+	git checkout main &&
 	has_cr .file2
 
 '
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 630c3ee..a9e10a0 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -2,6 +2,9 @@
 
 test_description='blob conversion via gitattributes'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 TEST_ROOT="$PWD"
@@ -31,7 +34,7 @@
 # Compare two files and ensure that `clean` and `smudge` respectively are
 # called at least once if specified in the `expect` file. The actual
 # invocation count is not relevant because their number can vary.
-# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. https://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
 test_cmp_count () {
 	expect=$1
 	actual=$2
@@ -46,7 +49,7 @@
 
 # Compare two files but exclude all `clean` invocations because Git can
 # call `clean` zero or more times.
-# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. https://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
 test_cmp_exclude_clean () {
 	expect=$1
 	actual=$2
@@ -378,8 +381,8 @@
 		test_cmp_count expected.log debug.log &&
 
 		git commit -m "test commit 2" &&
-		MASTER=$(git rev-parse --verify master) &&
-		META="ref=refs/heads/master treeish=$MASTER" &&
+		MAIN=$(git rev-parse --verify main) &&
+		META="ref=refs/heads/main treeish=$MAIN" &&
 		rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
 
 		filter_git checkout --quiet --no-progress . &&
@@ -404,7 +407,7 @@
 		EOF
 		test_cmp_exclude_clean expected.log debug.log &&
 
-		filter_git checkout --quiet --no-progress master &&
+		filter_git checkout --quiet --no-progress main &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -436,15 +439,15 @@
 		M3=$(git hash-object "testsubdir/test3 '\''sq'\'',\$x=.r") &&
 		EMPTY=$(git hash-object /dev/null) &&
 
-		MASTER=$(git rev-parse --verify master) &&
+		MAIN=$(git rev-parse --verify main) &&
 
 		cp "$TEST_ROOT/test.o" test5.r &&
 		git add test5.r &&
 		git commit -m "test commit 3" &&
 		git checkout empty-branch &&
-		filter_git rebase --onto empty-branch master^^ master &&
-		MASTER2=$(git rev-parse --verify master) &&
-		META="ref=refs/heads/master treeish=$MASTER2" &&
+		filter_git rebase --onto empty-branch main^^ main &&
+		MAIN2=$(git rev-parse --verify main) &&
+		META="ref=refs/heads/main treeish=$MAIN2" &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -458,8 +461,8 @@
 		test_cmp_exclude_clean expected.log debug.log &&
 
 		git reset --hard empty-branch &&
-		filter_git reset --hard $MASTER &&
-		META="treeish=$MASTER" &&
+		filter_git reset --hard $MAIN &&
+		META="treeish=$MAIN" &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -471,10 +474,10 @@
 		EOF
 		test_cmp_exclude_clean expected.log debug.log &&
 
-		git branch old-master $MASTER &&
+		git branch old-main $MAIN &&
 		git reset --hard empty-branch &&
-		filter_git reset --hard old-master &&
-		META="ref=refs/heads/old-master treeish=$MASTER" &&
+		filter_git reset --hard old-main &&
+		META="ref=refs/heads/old-main treeish=$MAIN" &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -487,9 +490,9 @@
 		test_cmp_exclude_clean expected.log debug.log &&
 
 		git checkout -b merge empty-branch &&
-		git branch -f master $MASTER2 &&
-		filter_git merge master &&
-		META="treeish=$MASTER2" &&
+		git branch -f main $MAIN2 &&
+		filter_git merge main &&
+		META="treeish=$MAIN2" &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -502,8 +505,8 @@
 		EOF
 		test_cmp_exclude_clean expected.log debug.log &&
 
-		filter_git archive master >/dev/null &&
-		META="ref=refs/heads/master treeish=$MASTER2" &&
+		filter_git archive main >/dev/null &&
+		META="ref=refs/heads/main treeish=$MAIN2" &&
 		cat >expected.log <<-EOF &&
 			START
 			init handshake complete
@@ -516,7 +519,7 @@
 		EOF
 		test_cmp_exclude_clean expected.log debug.log &&
 
-		TREE="$(git rev-parse $MASTER2^{tree})" &&
+		TREE="$(git rev-parse $MAIN2^{tree})" &&
 		filter_git archive $TREE >/dev/null &&
 		META="treeish=$TREE" &&
 		cat >expected.log <<-EOF &&
@@ -856,8 +859,8 @@
 	) &&
 
 	S=$(test_file_size "$TEST_ROOT/test.o") &&
-	PM="ref=refs/heads/master treeish=$(git -C repo rev-parse --verify master) " &&
-	M="${PM}blob=$(git -C repo rev-parse --verify master:test.a)" &&
+	PM="ref=refs/heads/main treeish=$(git -C repo rev-parse --verify main) " &&
+	M="${PM}blob=$(git -C repo rev-parse --verify main:test.a)" &&
 	cat >a.exp <<-EOF &&
 		START
 		init handshake complete
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 9fcd56f..d24d5ac 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -83,7 +83,7 @@
 	*) echo >&2 "Illegal 1": "$1" ; return false ;;
 	esac
 	grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq  >"$2".actual
-	test_i18ncmp "$2".expect "$2".actual
+	test_cmp "$2".expect "$2".actual
 }
 
 commit_check_warn () {
@@ -368,9 +368,9 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'setup master' '
+test_expect_success 'setup main' '
 	echo >.gitattributes &&
-	git checkout -b master &&
+	git checkout -b main &&
 	git add .gitattributes &&
 	git commit -m "add .gitattributes" . &&
 	printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE"     >LF &&
diff --git a/t/t0028-working-tree-encoding.sh b/t/t0028-working-tree-encoding.sh
index bfc4fb9..f970a98 100755
--- a/t/t0028-working-tree-encoding.sh
+++ b/t/t0028-working-tree-encoding.sh
@@ -2,6 +2,9 @@
 
 test_description='working-tree-encoding conversion via gitattributes'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 GIT_TRACE_WORKING_TREE_ENCODING=1 && export GIT_TRACE_WORKING_TREE_ENCODING
@@ -215,7 +218,7 @@
 	TEST_HASH=$(git hash-object --no-filters -w nonsense.utf16le) &&
 	git update-index --add --cacheinfo 100644 $TEST_HASH nonsense.utf16le &&
 	COMMIT=$(git commit-tree -p $(git rev-parse HEAD) -m "plain commit" $(git write-tree)) &&
-	git update-ref refs/heads/master $COMMIT &&
+	git update-ref refs/heads/main $COMMIT &&
 
 	test_must_fail git checkout HEAD^ 2>err.out &&
 	test_i18ngrep "error: .* overwritten by checkout:" err.out
@@ -231,7 +234,7 @@
 	TEST_HASH=$(git hash-object --no-filters -w nonsense.utf16) &&
 	git update-index --add --cacheinfo 100644 $TEST_HASH nonsense.utf16 &&
 	COMMIT=$(git commit-tree -p $(git rev-parse HEAD) -m "plain commit" $(git write-tree)) &&
-	git update-ref refs/heads/master $COMMIT &&
+	git update-ref refs/heads/main $COMMIT &&
 
 	git diff 2>err.out &&
 	test_i18ngrep "error: BOM is required" err.out
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index 14cafc1..ad4746d 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -61,7 +61,7 @@
 test_expect_success 'test help' '
 	test_must_fail test-tool parse-options -h >output 2>output.err &&
 	test_must_be_empty output.err &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 mv expect expect.err
@@ -84,7 +84,7 @@
 	cat expect.err >>expect &&
 	test_must_fail test-tool parse-options $* >output 2>output.err &&
 	test_must_be_empty output &&
-	test_i18ncmp expect output.err
+	test_cmp expect output.err
 }
 
 test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'
@@ -250,7 +250,7 @@
 test_expect_success 'detect possible typos' '
 	test_must_fail test-tool parse-options -boolean >output 2>output.err &&
 	test_must_be_empty output &&
-	test_i18ncmp typo.err output.err
+	test_cmp typo.err output.err
 '
 
 cat >typo.err <<\EOF
@@ -260,7 +260,7 @@
 test_expect_success 'detect possible typos' '
 	test_must_fail test-tool parse-options -ambiguous >output 2>output.err &&
 	test_must_be_empty output &&
-	test_i18ncmp typo.err output.err
+	test_cmp typo.err output.err
 '
 
 test_expect_success 'keep some options as arguments' '
diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh
index 5b927b7..c4fc34e 100755
--- a/t/t0041-usage.sh
+++ b/t/t0041-usage.sh
@@ -2,6 +2,9 @@
 
 test_description='Test commands behavior when given invalid argument value'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup ' '
@@ -41,8 +44,8 @@
 '
 
 test_expect_success 'branch --contains <existent_commit>' '
-	git branch --contains "master" >actual 2>actual.err &&
-	test_i18ngrep "master" actual &&
+	git branch --contains "main" >actual 2>actual.err &&
+	test_i18ngrep "main" actual &&
 	test_line_count = 0 actual.err
 '
 
@@ -54,7 +57,7 @@
 '
 
 test_expect_success 'branch --no-contains <existent_commit>' '
-	git branch --no-contains "master" >actual 2>actual.err &&
+	git branch --no-contains "main" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_line_count = 0 actual.err
 '
@@ -73,7 +76,7 @@
 '
 
 test_expect_success 'for-each-ref --contains <existent_object>' '
-	git for-each-ref --contains "master" >actual 2>actual.err &&
+	git for-each-ref --contains "main" >actual 2>actual.err &&
 	test_line_count = 2 actual &&
 	test_line_count = 0 actual.err
 '
@@ -86,7 +89,7 @@
 '
 
 test_expect_success 'for-each-ref --no-contains <existent_object>' '
-	git for-each-ref --no-contains "master" >actual 2>actual.err &&
+	git for-each-ref --no-contains "main" >actual 2>actual.err &&
 	test_line_count = 0 actual &&
 	test_line_count = 0 actual.err
 '
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
index 608673f..afc343c 100755
--- a/t/t0050-filesystem.sh
+++ b/t/t0050-filesystem.sh
@@ -2,6 +2,9 @@
 
 test_description='Various filesystem issues'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 auml=$(printf '\303\244')
@@ -65,7 +68,7 @@
 	git mv camelcase tmp &&
 	git mv tmp CamelCase &&
 	git commit -m "rename" &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'rename (case change)' '
@@ -118,7 +121,7 @@
 	git mv $aumlcdiar tmp &&
 	git mv tmp "$auml" &&
 	git commit -m rename &&
-	git checkout -f master
+	git checkout -f main
 '
 
 $test_unicode 'rename (silent unicode normalization)' '
@@ -147,7 +150,7 @@
 		git add gitweb &&
 		git commit -m "add gitweb/subdir/file" &&
 
-		git checkout master
+		git checkout main
 	)
 '
 
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 56db5c8..0ff06b5 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -312,8 +312,8 @@
 test_git_path GIT_COMMON_DIR=bar info//sparse-checkout    .git/info//sparse-checkout
 test_git_path GIT_COMMON_DIR=bar remotes/bar              bar/remotes/bar
 test_git_path GIT_COMMON_DIR=bar branches/bar             bar/branches/bar
-test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master   bar/logs/refs/heads/master
-test_git_path GIT_COMMON_DIR=bar refs/heads/master        bar/refs/heads/master
+test_git_path GIT_COMMON_DIR=bar logs/refs/heads/main     bar/logs/refs/heads/main
+test_git_path GIT_COMMON_DIR=bar refs/heads/main          bar/refs/heads/main
 test_git_path GIT_COMMON_DIR=bar refs/bisect/foo          .git/refs/bisect/foo
 test_git_path GIT_COMMON_DIR=bar hooks/me                 bar/hooks/me
 test_git_path GIT_COMMON_DIR=bar config                   bar/config
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index 5a63369..9bf66c9 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -10,40 +10,36 @@
 cmp_cache_tree () {
 	test-tool dump-cache-tree | sed -e '/#(ref)/d' >actual &&
 	sed "s/$OID_REGEX/SHA/" <actual >filtered &&
-	test_cmp "$1" filtered
+	test_cmp "$1" filtered &&
+	rm filtered
 }
 
 # We don't bother with actually checking the SHA1:
 # test-tool dump-cache-tree already verifies that all existing data is
 # correct.
-generate_expected_cache_tree_rec () {
-	dir="$1${1:+/}" &&
-	parent="$2" &&
-	# ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
-	# We want to count only foo because it's the only direct child
-	git ls-files >files &&
-	subtrees=$(grep / files|cut -d / -f 1|uniq) &&
-	subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
-	entries=$(wc -l <files) &&
-	printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
-	for subtree in $subtrees
-	do
-		cd "$subtree"
-		generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
-		cd ..
-	done &&
-	dir=$parent
-}
-
 generate_expected_cache_tree () {
-	(
-		generate_expected_cache_tree_rec
-	)
+	pathspec="$1" &&
+	dir="$2${2:+/}" &&
+	git ls-tree --name-only HEAD -- "$pathspec" >files &&
+	git ls-tree --name-only -d HEAD -- "$pathspec" >subtrees &&
+	printf "SHA %s (%d entries, %d subtrees)\n" "$dir" $(wc -l <files) $(wc -l <subtrees) &&
+	while read subtree
+	do
+		generate_expected_cache_tree "$pathspec/$subtree/" "$subtree" || return 1
+	done <subtrees
 }
 
 test_cache_tree () {
-	generate_expected_cache_tree >expect &&
-	cmp_cache_tree expect
+	generate_expected_cache_tree "." >expect &&
+	cmp_cache_tree expect &&
+	rm expect actual files subtrees &&
+	git status --porcelain -- ':!status' ':!expected.status' >status &&
+	if test -n "$1"
+	then
+		test_cmp "$1" status
+	else
+		test_must_be_empty status
+	fi
 }
 
 test_invalid_cache_tree () {
@@ -54,7 +50,7 @@
 }
 
 test_no_cache_tree () {
-	: >expect &&
+	>expect &&
 	cmp_cache_tree expect
 }
 
@@ -83,18 +79,6 @@
 	test_invalid_cache_tree
 '
 
-cat >before <<\EOF
-SHA  (3 entries, 2 subtrees)
-SHA dir1/ (1 entries, 0 subtrees)
-SHA dir2/ (1 entries, 0 subtrees)
-EOF
-
-cat >expect <<\EOF
-invalid                                   (2 subtrees)
-invalid                                  dir1/ (0 subtrees)
-SHA dir2/ (1 entries, 0 subtrees)
-EOF
-
 test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
 	git tag no-children &&
 	test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
@@ -102,9 +86,20 @@
 	test_commit dir1/a &&
 	test_commit dir2/b &&
 	echo "I changed this file" >dir1/a &&
+	test_when_finished "rm before" &&
+	cat >before <<-\EOF &&
+	SHA  (3 entries, 2 subtrees)
+	SHA dir1/ (1 entries, 0 subtrees)
+	SHA dir2/ (1 entries, 0 subtrees)
+	EOF
 	cmp_cache_tree before &&
 	echo "I changed this file" >dir1/a &&
 	git add dir1/a &&
+	cat >expect <<-\EOF &&
+	invalid                                   (2 subtrees)
+	invalid                                  dir1/ (0 subtrees)
+	SHA dir2/ (1 entries, 0 subtrees)
+	EOF
 	cmp_cache_tree expect
 '
 
@@ -133,6 +128,7 @@
 '
 
 test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
+	test_when_finished "git reset --hard" &&
 	cat <<-\EOT >foo.c &&
 	int foo()
 	{
@@ -159,7 +155,10 @@
 	EOT
 	test_write_lines p 1 "" s n y q |
 	git commit --interactive -m foo &&
-	test_cache_tree
+	cat <<-\EOF >expected.status &&
+	 M foo.c
+	EOF
+	test_cache_tree expected.status
 '
 
 test_expect_success PERL 'commit -p with shrinking cache-tree' '
@@ -250,7 +249,10 @@
 	git add one.t &&
 	echo "some other change" >two.t &&
 	git commit two.t -m partial &&
-	test_cache_tree
+	cat <<-\EOF >expected.status &&
+	M  one.t
+	EOF
+	test_cache_tree expected.status
 '
 
 test_expect_success 'no phantom error when switching trees' '
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 58c0b7e..69beb59 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -2,13 +2,16 @@
 
 test_description='previous branch syntax @{-n}'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
 	test_commit A &&
 	git checkout -b junk &&
 	git checkout - &&
-	test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+	test "$(git symbolic-ref HEAD)" = refs/heads/main &&
 	git branch -d @{-1} &&
 	test_must_fail git rev-parse --verify refs/heads/junk
 '
@@ -17,9 +20,9 @@
 	git reflog expire --expire=now &&
 	git checkout -b junk2 &&
 	git checkout - &&
-	test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+	test "$(git symbolic-ref HEAD)" = refs/heads/main &&
 	test_must_fail git branch -d @{-12} &&
-	git rev-parse --verify refs/heads/master
+	git rev-parse --verify refs/heads/main
 '
 
 test_expect_success 'merge @{-1}' '
@@ -28,19 +31,19 @@
 	git checkout A &&
 	test_commit C &&
 	test_commit D &&
-	git branch -f master B &&
+	git branch -f main B &&
 	git branch -f other &&
 	git checkout other &&
-	git checkout master &&
+	git checkout main &&
 	git merge @{-1} &&
 	git cat-file commit HEAD | grep "Merge branch '\''other'\''"
 '
 
 test_expect_success 'merge @{-1}~1' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard B &&
 	git checkout other &&
-	git checkout master &&
+	git checkout main &&
 	git merge @{-1}~1 &&
 	git cat-file commit HEAD >actual &&
 	grep "Merge branch '\''other'\''" actual
@@ -48,11 +51,11 @@
 
 test_expect_success 'merge @{-100} before checking out that many branches yet' '
 	git reflog expire --expire=now &&
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard B &&
 	git branch -f other C &&
 	git checkout other &&
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git merge @{-100}
 '
 
diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh
index 90da1c7..6c74df0 100755
--- a/t/t0201-gettext-fallbacks.sh
+++ b/t/t0201-gettext-fallbacks.sh
@@ -18,7 +18,7 @@
     test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
 '
 
-test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
     echo fallthrough >expect &&
     echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&
     test_cmp expect actual
@@ -27,25 +27,25 @@
 test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
     printf "test" >expect &&
     gettext "test" >actual &&
-    test_i18ncmp expect actual &&
+    test_cmp expect actual &&
     printf "test more words" >expect &&
     gettext "test more words" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
     printf "test" >expect &&
     eval_gettext "test" >actual &&
-    test_i18ncmp expect actual &&
+    test_cmp expect actual &&
     printf "test more words" >expect &&
     eval_gettext "test more words" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
     printf "test YesPlease" >expect &&
     GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
@@ -53,7 +53,7 @@
     export cmdline &&
     printf "When you have resolved this problem, run git am --resolved." >expect &&
     eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
@@ -61,7 +61,7 @@
     export cmdline &&
     printf "When you have resolved this problem, run \"git am --resolved\"." >expect &&
     eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_done
diff --git a/t/t0205-gettext-poison.sh b/t/t0205-gettext-poison.sh
deleted file mode 100755
index f9fa16a..0000000
--- a/t/t0205-gettext-poison.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2010 Ævar Arnfjörð Bjarmason
-#
-
-test_description='Gettext Shell poison'
-
-GIT_TEST_GETTEXT_POISON=true
-export GIT_TEST_GETTEXT_POISON
-. ./lib-gettext.sh
-
-test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is poison' '
-    test "$GIT_INTERNAL_GETTEXT_SH_SCHEME" = "poison"
-'
-
-test_expect_success 'gettext: our gettext() fallback has poison semantics' '
-    printf "# GETTEXT POISON #" >expect &&
-    gettext "test" >actual &&
-    test_cmp expect actual &&
-    printf "# GETTEXT POISON #" >expect &&
-    gettext "test more words" >actual &&
-    test_cmp expect actual
-'
-
-test_expect_success 'eval_gettext: our eval_gettext() fallback has poison semantics' '
-    printf "# GETTEXT POISON #" >expect &&
-    eval_gettext "test" >actual &&
-    test_cmp expect actual &&
-    printf "# GETTEXT POISON #" >expect &&
-    eval_gettext "test more words" >actual &&
-    test_cmp expect actual
-'
-
-test_expect_success "gettext: invalid GIT_TEST_GETTEXT_POISON value doesn't infinitely loop" "
-	test_must_fail env GIT_TEST_GETTEXT_POISON=xyz git version 2>error &&
-	grep \"fatal: bad numeric config value 'xyz' for 'GIT_TEST_GETTEXT_POISON': invalid unit\" error
-"
-
-test_done
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index ce7574e..0cf3a63 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -147,6 +147,25 @@
 	test_cmp expect actual
 '
 
+# Verb 007bug
+#
+# Check that BUG writes to trace2
+
+test_expect_success 'BUG messages are written to trace2' '
+	test_when_finished "rm trace.normal actual expect" &&
+	test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 007bug &&
+	perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+	cat >expect <<-EOF &&
+		version $V
+		start _EXE_ trace2 007bug
+		cmd_name trace2 (trace2)
+		error the bug message
+		exit elapsed:_TIME_ code:99
+		atexit elapsed:_TIME_ code:99
+	EOF
+	test_cmp expect actual
+'
+
 sane_unset GIT_TRACE2_BRIEF
 
 # Now test without environment variables and get all Trace2 settings
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index a18f8a4..3485c05 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -578,7 +578,7 @@
 	quit: host=example.com
 	fatal: credential helper '\''quit'\'' told us to quit
 	EOF
-	test_i18ncmp expect stderr
+	test_cmp expect stderr
 '
 
 test_expect_success 'empty helper spec resets helper list' '
@@ -606,7 +606,7 @@
 	warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
 	fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
 	EOF
-	test_i18ncmp expect stderr
+	test_cmp expect stderr
 '
 
 test_expect_success 'host-less URLs are parsed as empty host' '
@@ -633,7 +633,7 @@
 	cat >expect <<-\EOF &&
 	fatal: refusing to work with credential missing host field
 	EOF
-	test_i18ncmp expect stderr
+	test_cmp expect stderr
 '
 
 test_expect_success 'credential system refuses to work with missing protocol' '
@@ -643,7 +643,7 @@
 	cat >expect <<-\EOF &&
 	fatal: refusing to work with credential missing protocol field
 	EOF
-	test_i18ncmp expect stderr
+	test_cmp expect stderr
 '
 
 # usage: check_host_and_path <url> <expected-host> <expected-path>
diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh
index 1ed1df3..22058b5 100755
--- a/t/t0500-progress-display.sh
+++ b/t/t0500-progress-display.sh
@@ -29,7 +29,7 @@
 	test-tool progress "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display with total' '
@@ -48,7 +48,7 @@
 	test-tool progress --total=3 "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #1' '
@@ -72,7 +72,7 @@
 		<in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #2' '
@@ -100,7 +100,7 @@
 		<in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #3 - even the first is too long' '
@@ -126,7 +126,7 @@
 		<in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #4 - title line matches terminal width' '
@@ -150,7 +150,7 @@
 		<in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 # Progress counter goes backwards, this should not happen in practice.
@@ -172,7 +172,7 @@
 	test-tool progress --total=1000 "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display with throughput' '
@@ -201,7 +201,7 @@
 	test-tool progress "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress display with throughput and total' '
@@ -226,7 +226,7 @@
 	test-tool progress --total=40 "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'cover up after throughput shortens' '
@@ -255,7 +255,7 @@
 	test-tool progress "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'cover up after throughput shortens a lot' '
@@ -280,7 +280,7 @@
 	test-tool progress "Working hard" <in 2>stderr &&
 
 	show_cr <stderr >out &&
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'progress generates traces' '
@@ -303,8 +303,7 @@
 		"Working hard" <in 2>stderr &&
 
 	# t0212/parse_events.perl intentionally omits regions and data.
-	grep -e "region_enter" -e "\"category\":\"progress\"" trace.event &&
-	grep -e "region_leave" -e "\"category\":\"progress\"" trace.event &&
+	test_region progress "Working hard" trace.event &&
 	grep "\"key\":\"total_objects\",\"value\":\"40\"" trace.event &&
 	grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event
 '
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 181956b..11bf104 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -2,6 +2,9 @@
 
 test_description='read-tree -m -u checks working tree files'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
@@ -20,17 +23,17 @@
 	git branch side &&
 	git tag -f branch-point &&
 
-	echo file2 is not tracked on the master branch anymore &&
+	echo file2 is not tracked on the main branch anymore &&
 	rm -f file2 subdir/file2 &&
 	git update-index --remove file2 subdir/file2 &&
-	git commit -a -m "master removes file2 and subdir/file2"
+	git commit -a -m "main removes file2 and subdir/file2"
 '
 
 test_expect_success 'two-way not clobbering' '
 
-	echo >file2 master creates untracked file2 &&
-	echo >subdir/file2 master creates untracked subdir/file2 &&
-	if err=$(read_tree_u_must_succeed -m -u master side 2>&1)
+	echo >file2 main creates untracked file2 &&
+	echo >subdir/file2 main creates untracked subdir/file2 &&
+	if err=$(read_tree_u_must_succeed -m -u main side 2>&1)
 	then
 		echo should have complained
 		false
@@ -43,7 +46,7 @@
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
 
-	if err=$(read_tree_u_must_succeed -m --exclude-per-directory=.gitignore master side 2>&1)
+	if err=$(read_tree_u_must_succeed -m --exclude-per-directory=.gitignore main side 2>&1)
 	then
 		echo should have complained
 		false
@@ -54,7 +57,7 @@
 
 test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
 
-	if err=$(read_tree_u_must_succeed -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1)
+	if err=$(read_tree_u_must_succeed -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore main side 2>&1)
 	then
 		echo should have complained
 		false
@@ -65,7 +68,7 @@
 
 test_expect_success 'two-way clobbering a ignored file' '
 
-	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore master side
+	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore main side
 '
 
 rm -f .gitignore
@@ -81,21 +84,21 @@
 	git update-index --add file3 subdir/file3 &&
 	git commit -a -m "side adds file3 and removes file2" &&
 
-	git checkout master &&
-	echo >file2 file two is untracked on the master side &&
-	echo >subdir/file2 file two is untracked on the master side &&
+	git checkout main &&
+	echo >file2 file two is untracked on the main side &&
+	echo >subdir/file2 file two is untracked on the main side &&
 
-	read_tree_u_must_succeed -m -u branch-point master side
+	read_tree_u_must_succeed -m -u branch-point main side
 '
 
 test_expect_success 'three-way not clobbering a working tree file' '
 
 	git reset --hard &&
 	rm -f file2 subdir/file2 file3 subdir/file3 &&
-	git checkout master &&
-	echo >file3 file three created in master, untracked &&
-	echo >subdir/file3 file three created in master, untracked &&
-	if err=$(read_tree_u_must_succeed -m -u branch-point master side 2>&1)
+	git checkout main &&
+	echo >file3 file three created in main, untracked &&
+	echo >subdir/file3 file three created in main, untracked &&
+	if err=$(read_tree_u_must_succeed -m -u branch-point main side 2>&1)
 	then
 		echo should have complained
 		false
@@ -110,11 +113,11 @@
 
 	git reset --hard &&
 	rm -f file2 subdir/file2 file3 subdir/file3 &&
-	git checkout master &&
-	echo >file3 file three created in master, untracked &&
-	echo >subdir/file3 file three created in master, untracked &&
+	git checkout main &&
+	echo >file3 file three created in main, untracked &&
+	echo >subdir/file3 file three created in main, untracked &&
 
-	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore branch-point master side
+	read_tree_u_must_succeed -m -u --exclude-per-directory=.gitignore branch-point main side
 '
 
 test_expect_success '3-way not overwriting local changes (setup)' '
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 2f501d2..5d2dc99 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -166,7 +166,7 @@
 
 $tag_description"
 
-tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
+tag_sha1=$(echo_without_newline "$tag_content" | git hash-object -t tag --stdin -w)
 tag_size=$(strlen "$tag_content")
 
 run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
index cf96016..4512fb0b 100755
--- a/t/t1008-read-tree-overlay.sh
+++ b/t/t1008-read-tree-overlay.sh
@@ -2,6 +2,9 @@
 
 test_description='test multi-tree read-tree without merging'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
@@ -22,7 +25,7 @@
 '
 
 test_expect_success 'multi-read' '
-	read_tree_must_succeed initial master side &&
+	read_tree_must_succeed initial main side &&
 	test_write_lines a b/c >expect &&
 	git ls-files >actual &&
 	test_cmp expect actual
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
index 59b3aa4..2935f68 100755
--- a/t/t1009-read-tree-new-index.sh
+++ b/t/t1009-read-tree-new-index.sh
@@ -2,6 +2,9 @@
 
 test_description='test read-tree into a fresh index file'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -12,13 +15,13 @@
 
 test_expect_success 'non-existent index file' '
 	rm -f new-index &&
-	GIT_INDEX_FILE=new-index git read-tree master
+	GIT_INDEX_FILE=new-index git read-tree main
 '
 
 test_expect_success 'empty index file' '
 	rm -f new-index &&
 	> new-index &&
-	GIT_INDEX_FILE=new-index git read-tree master
+	GIT_INDEX_FILE=new-index git read-tree main
 '
 
 test_done
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
index 140f459..24092c0 100755
--- a/t/t1011-read-tree-sparse-checkout.sh
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -2,7 +2,7 @@
 
 test_description='sparse checkout tests
 
-* (tag: removed, master) removed
+* (tag: removed, main) removed
 | D	sub/added
 * (HEAD, tag: top) modified and added
 | M	init.t
@@ -253,7 +253,7 @@
 
 After fixing the above paths, you may want to run `git sparse-checkout reapply`.
 EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'checkout without --ignore-skip-worktree-bits' '
diff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh
index 301e071..0b89289 100755
--- a/t/t1021-rerere-in-workdir.sh
+++ b/t/t1021-rerere-in-workdir.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='rerere run in a workdir'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success SYMLINKS setup '
@@ -19,7 +22,7 @@
 	test_tick &&
 	git commit -a -m goodbye &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success SYMLINKS 'rerere in workdir' '
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index f35a73d..3deb490 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -2,6 +2,9 @@
 
 test_description='sparse checkout scope tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -20,18 +23,18 @@
 	git commit -m "modification"
 '
 
-test_expect_success 'perform sparse checkout of master' '
+test_expect_success 'perform sparse checkout of main' '
 	git config --local --bool core.sparsecheckout true &&
 	echo "!/*" >.git/info/sparse-checkout &&
 	echo "/a" >>.git/info/sparse-checkout &&
 	echo "/c" >>.git/info/sparse-checkout &&
-	git checkout master &&
+	git checkout main &&
 	test_path_is_file a &&
 	test_path_is_missing b &&
 	test_path_is_file c
 '
 
-test_expect_success 'merge feature branch into sparse checkout of master' '
+test_expect_success 'merge feature branch into sparse checkout of main' '
 	git merge feature &&
 	test_path_is_file a &&
 	test_path_is_missing b &&
@@ -39,10 +42,10 @@
 	test "$(cat c)" = "modified"
 '
 
-test_expect_success 'return to full checkout of master' '
+test_expect_success 'return to full checkout of main' '
 	git checkout feature &&
 	echo "/*" >.git/info/sparse-checkout &&
-	git checkout master &&
+	git checkout main &&
 	test_path_is_file a &&
 	test_path_is_file b &&
 	test_path_is_file c &&
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 84acfc4..fc64e9e 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -2,6 +2,9 @@
 
 test_description='sparse checkout builtin tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 list_files() {
@@ -117,7 +120,7 @@
 	test_path_is_missing clone_no_checkout/.git/index &&
 
 	# No branch is checked out until we manually switch to one
-	git -C clone_no_checkout switch master &&
+	git -C clone_no_checkout switch main &&
 	test_path_is_file clone_no_checkout/.git/index &&
 	check_files clone_no_checkout a folder1
 '
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
new file mode 100755
index 0000000..8cd3e5a
--- /dev/null
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -0,0 +1,301 @@
+#!/bin/sh
+
+test_description='compare full workdir to sparse workdir'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	git init initial-repo &&
+	(
+		cd initial-repo &&
+		echo a >a &&
+		echo "after deep" >e &&
+		echo "after folder1" >g &&
+		echo "after x" >z &&
+		mkdir folder1 folder2 deep x &&
+		mkdir deep/deeper1 deep/deeper2 &&
+		mkdir deep/deeper1/deepest &&
+		echo "after deeper1" >deep/e &&
+		echo "after deepest" >deep/deeper1/e &&
+		cp a folder1 &&
+		cp a folder2 &&
+		cp a x &&
+		cp a deep &&
+		cp a deep/deeper1 &&
+		cp a deep/deeper2 &&
+		cp a deep/deeper1/deepest &&
+		cp -r deep/deeper1/deepest deep/deeper2 &&
+		git add . &&
+		git commit -m "initial commit" &&
+		git checkout -b base &&
+		for dir in folder1 folder2 deep
+		do
+			git checkout -b update-$dir &&
+			echo "updated $dir" >$dir/a &&
+			git commit -a -m "update $dir" || return 1
+		done &&
+
+		git checkout -b rename-base base &&
+		echo >folder1/larger-content <<-\EOF &&
+		matching
+		lines
+		help
+		inexact
+		renames
+		EOF
+		cp folder1/larger-content folder2/ &&
+		cp folder1/larger-content deep/deeper1/ &&
+		git add . &&
+		git commit -m "add interesting rename content" &&
+
+		git checkout -b rename-out-to-out rename-base &&
+		mv folder1/a folder2/b &&
+		mv folder1/larger-content folder2/edited-content &&
+		echo >>folder2/edited-content &&
+		git add . &&
+		git commit -m "rename folder1/... to folder2/..." &&
+
+		git checkout -b rename-out-to-in rename-base &&
+		mv folder1/a deep/deeper1/b &&
+		mv folder1/larger-content deep/deeper1/edited-content &&
+		echo >>deep/deeper1/edited-content &&
+		git add . &&
+		git commit -m "rename folder1/... to deep/deeper1/..." &&
+
+		git checkout -b rename-in-to-out rename-base &&
+		mv deep/deeper1/a folder1/b &&
+		mv deep/deeper1/larger-content folder1/edited-content &&
+		echo >>folder1/edited-content &&
+		git add . &&
+		git commit -m "rename deep/deeper1/... to folder1/..." &&
+
+		git checkout -b deepest base &&
+		echo "updated deepest" >deep/deeper1/deepest/a &&
+		git commit -a -m "update deepest" &&
+
+		git checkout -f base &&
+		git reset --hard
+	)
+'
+
+init_repos () {
+	rm -rf full-checkout sparse-checkout sparse-index &&
+
+	# create repos in initial state
+	cp -r initial-repo full-checkout &&
+	git -C full-checkout reset --hard &&
+
+	cp -r initial-repo sparse-checkout &&
+	git -C sparse-checkout reset --hard &&
+	git -C sparse-checkout sparse-checkout init --cone &&
+
+	# initialize sparse-checkout definitions
+	git -C sparse-checkout sparse-checkout set deep
+}
+
+run_on_sparse () {
+	(
+		cd sparse-checkout &&
+		$* >../sparse-checkout-out 2>../sparse-checkout-err
+	)
+}
+
+run_on_all () {
+	(
+		cd full-checkout &&
+		$* >../full-checkout-out 2>../full-checkout-err
+	) &&
+	run_on_sparse $*
+}
+
+test_all_match () {
+	run_on_all $* &&
+	test_cmp full-checkout-out sparse-checkout-out &&
+	test_cmp full-checkout-err sparse-checkout-err
+}
+
+test_expect_success 'status with options' '
+	init_repos &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git status --porcelain=v2 -z -u &&
+	test_all_match git status --porcelain=v2 -uno &&
+	run_on_all "touch README.md" &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git status --porcelain=v2 -z -u &&
+	test_all_match git status --porcelain=v2 -uno &&
+	test_all_match git add README.md &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git status --porcelain=v2 -z -u &&
+	test_all_match git status --porcelain=v2 -uno
+'
+
+test_expect_success 'add, commit, checkout' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>$1
+	EOF
+	run_on_all "../edit-contents README.md" &&
+
+	test_all_match git add README.md &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git commit -m "Add README.md" &&
+
+	test_all_match git checkout HEAD~1 &&
+	test_all_match git checkout - &&
+
+	run_on_all "../edit-contents README.md" &&
+
+	test_all_match git add -A &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git commit -m "Extend README.md" &&
+
+	test_all_match git checkout HEAD~1 &&
+	test_all_match git checkout - &&
+
+	run_on_all "../edit-contents deep/newfile" &&
+
+	test_all_match git status --porcelain=v2 -uno &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git add . &&
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git commit -m "add deep/newfile" &&
+
+	test_all_match git checkout HEAD~1 &&
+	test_all_match git checkout -
+'
+
+test_expect_success 'checkout and reset --hard' '
+	init_repos &&
+
+	test_all_match git checkout update-folder1 &&
+	test_all_match git status --porcelain=v2 &&
+
+	test_all_match git checkout update-deep &&
+	test_all_match git status --porcelain=v2 &&
+
+	test_all_match git checkout -b reset-test &&
+	test_all_match git reset --hard deepest &&
+	test_all_match git reset --hard update-folder1 &&
+	test_all_match git reset --hard update-folder2
+'
+
+test_expect_success 'diff --staged' '
+	init_repos &&
+
+	write_script edit-contents <<-\EOF &&
+	echo text >>README.md
+	EOF
+	run_on_all "../edit-contents" &&
+
+	test_all_match git diff &&
+	test_all_match git diff --staged &&
+	test_all_match git add README.md &&
+	test_all_match git diff &&
+	test_all_match git diff --staged
+'
+
+test_expect_success 'diff with renames' '
+	init_repos &&
+
+	for branch in rename-out-to-out rename-out-to-in rename-in-to-out
+	do
+		test_all_match git checkout rename-base &&
+		test_all_match git checkout $branch -- .&&
+		test_all_match git diff --staged --no-renames &&
+		test_all_match git diff --staged --find-renames || return 1
+	done
+'
+
+test_expect_success 'log with pathspec outside sparse definition' '
+	init_repos &&
+
+	test_all_match git log -- a &&
+	test_all_match git log -- folder1/a &&
+	test_all_match git log -- folder2/a &&
+	test_all_match git log -- deep/a &&
+	test_all_match git log -- deep/deeper1/a &&
+	test_all_match git log -- deep/deeper1/deepest/a &&
+
+	test_all_match git checkout update-folder1 &&
+	test_all_match git log -- folder1/a
+'
+
+test_expect_success 'blame with pathspec inside sparse definition' '
+	init_repos &&
+
+	test_all_match git blame a &&
+	test_all_match git blame deep/a &&
+	test_all_match git blame deep/deeper1/a &&
+	test_all_match git blame deep/deeper1/deepest/a
+'
+
+# TODO: blame currently does not support blaming files outside of the
+# sparse definition. It complains that the file doesn't exist locally.
+test_expect_failure 'blame with pathspec outside sparse definition' '
+	init_repos &&
+
+	test_all_match git blame folder1/a &&
+	test_all_match git blame folder2/a &&
+	test_all_match git blame deep/deeper2/a &&
+	test_all_match git blame deep/deeper2/deepest/a
+'
+
+# TODO: reset currently does not behave as expected when in a
+# sparse-checkout.
+test_expect_failure 'checkout and reset (mixed)' '
+	init_repos &&
+
+	test_all_match git checkout -b reset-test update-deep &&
+	test_all_match git reset deepest &&
+	test_all_match git reset update-folder1 &&
+	test_all_match git reset update-folder2
+'
+
+test_expect_success 'merge' '
+	init_repos &&
+
+	test_all_match git checkout -b merge update-deep &&
+	test_all_match git merge -m "folder1" update-folder1 &&
+	test_all_match git rev-parse HEAD^{tree} &&
+	test_all_match git merge -m "folder2" update-folder2 &&
+	test_all_match git rev-parse HEAD^{tree}
+'
+
+test_expect_success 'merge with outside renames' '
+	init_repos &&
+
+	for type in out-to-out out-to-in in-to-out
+	do
+		test_all_match git reset --hard &&
+		test_all_match git checkout -f -b merge-$type update-deep &&
+		test_all_match git merge -m "$type" rename-$type &&
+		test_all_match git rev-parse HEAD^{tree} || return 1
+	done
+'
+
+test_expect_success 'clean' '
+	init_repos &&
+
+	echo bogus >>.gitignore &&
+	run_on_all cp ../.gitignore . &&
+	test_all_match git add .gitignore &&
+	test_all_match git commit -m ignore-bogus-files &&
+
+	run_on_sparse mkdir folder1 &&
+	run_on_all touch folder1/bogus &&
+
+	test_all_match git status --porcelain=v2 &&
+	test_all_match git clean -f &&
+	test_all_match git status --porcelain=v2 &&
+
+	test_all_match git clean -xf &&
+	test_all_match git status --porcelain=v2 &&
+
+	test_all_match git clean -xdf &&
+	test_all_match git status --porcelain=v2 &&
+
+	test_path_is_dir sparse-checkout/folder1
+'
+
+test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 1a4156c..e0dd5d6 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -5,6 +5,9 @@
 
 test_description='Test git config in different settings'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'clear default config' '
@@ -672,6 +675,13 @@
 	test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
 '
 
+test_expect_success 'invalid unit boolean' '
+	git config commit.gpgsign "1true" &&
+	test_cmp_config 1true commit.gpgsign &&
+	test_must_fail git config --bool --get commit.gpgsign 2>actual &&
+	test_i18ngrep "bad boolean config value .1true. for .commit.gpgsign." actual
+'
+
 test_expect_success 'line number is reported correctly' '
 	printf "[bool]\n\tvar\n" >invalid &&
 	test_must_fail git config -f invalid --path bool.var 2>actual &&
@@ -1046,8 +1056,8 @@
 	echo foo > foo &&
 	git add foo &&
 	git commit -m 'initial commit' &&
-	git config branch.master.mergeoptions 'echo \"' &&
-	test_must_fail git merge master
+	git config branch.main.mergeoptions 'echo \"' &&
+	test_must_fail git merge main
 "
 
 test_expect_success 'git -c "key=value" support' '
@@ -1286,6 +1296,58 @@
 	GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list
 '
 
+test_expect_success 'GIT_CONFIG_PARAMETERS handles old-style entries' '
+	v="${SQ}key.one=foo${SQ}" &&
+	v="$v  ${SQ}key.two=bar${SQ}" &&
+	v="$v ${SQ}key.ambiguous=section.whatever=value${SQ}" &&
+	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+	cat >expect <<-EOF &&
+	key.one foo
+	key.two bar
+	key.ambiguous section.whatever=value
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'GIT_CONFIG_PARAMETERS handles new-style entries' '
+	v="${SQ}key.one${SQ}=${SQ}foo${SQ}" &&
+	v="$v  ${SQ}key.two${SQ}=${SQ}bar${SQ}" &&
+	v="$v ${SQ}key.ambiguous=section.whatever${SQ}=${SQ}value${SQ}" &&
+	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+	cat >expect <<-EOF &&
+	key.one foo
+	key.two bar
+	key.ambiguous=section.whatever value
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'old and new-style entries can mix' '
+	v="${SQ}key.oldone=oldfoo${SQ}" &&
+	v="$v ${SQ}key.newone${SQ}=${SQ}newfoo${SQ}" &&
+	v="$v ${SQ}key.oldtwo=oldbar${SQ}" &&
+	v="$v ${SQ}key.newtwo${SQ}=${SQ}newbar${SQ}" &&
+	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+	cat >expect <<-EOF &&
+	key.oldone oldfoo
+	key.newone newfoo
+	key.oldtwo oldbar
+	key.newtwo newbar
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'old and new bools with ambiguous subsection' '
+	v="${SQ}key.with=equals.oldbool${SQ}" &&
+	v="$v ${SQ}key.with=equals.newbool${SQ}=" &&
+	GIT_CONFIG_PARAMETERS=$v git config --get-regexp "key.*" >actual &&
+	cat >expect <<-EOF &&
+	key.with equals.oldbool
+	key.with=equals.newbool
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'detect bogus GIT_CONFIG_PARAMETERS' '
 	cat >expect <<-\EOF &&
 	env.one one
@@ -1308,6 +1370,173 @@
 		git config --get-regexp "env.*"
 '
 
+test_expect_success 'git --config-env=key=envvar support' '
+	cat >expect <<-\EOF &&
+	value
+	value
+	false
+	EOF
+	{
+		ENVVAR=value git --config-env=core.name=ENVVAR config core.name &&
+		ENVVAR=value git --config-env=foo.CamelCase=ENVVAR config foo.camelcase &&
+		ENVVAR= git --config-env=foo.flag=ENVVAR config --bool foo.flag
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git --config-env fails with invalid parameters' '
+	test_must_fail git --config-env=foo.flag config --bool foo.flag 2>error &&
+	test_i18ngrep "invalid config format: foo.flag" error &&
+	test_must_fail git --config-env=foo.flag= config --bool foo.flag 2>error &&
+	test_i18ngrep "missing environment variable name for configuration ${SQ}foo.flag${SQ}" error &&
+	sane_unset NONEXISTENT &&
+	test_must_fail git --config-env=foo.flag=NONEXISTENT config --bool foo.flag 2>error &&
+	test_i18ngrep "missing environment variable ${SQ}NONEXISTENT${SQ} for configuration ${SQ}foo.flag${SQ}" error
+'
+
+test_expect_success 'git -c and --config-env work together' '
+	cat >expect <<-\EOF &&
+	bar.cmd cmd-value
+	bar.env env-value
+	EOF
+	ENVVAR=env-value git \
+		-c bar.cmd=cmd-value \
+		--config-env=bar.env=ENVVAR \
+		config --get-regexp "^bar.*" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git -c and --config-env override each other' '
+	cat >expect <<-\EOF &&
+	env
+	cmd
+	EOF
+	{
+		ENVVAR=env git -c bar.bar=cmd --config-env=bar.bar=ENVVAR config bar.bar &&
+		ENVVAR=env git --config-env=bar.bar=ENVVAR -c bar.bar=cmd config bar.bar
+	} >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--config-env handles keys with equals' '
+	echo value=with=equals >expect &&
+	ENVVAR=value=with=equals git \
+		--config-env=section.subsection=with=equals.key=ENVVAR \
+		config section.subsection=with=equals.key >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'git config handles environment config pairs' '
+	GIT_CONFIG_COUNT=2 \
+		GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="foo" \
+		GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="bar" \
+		git config --get-regexp "pair.*" >actual &&
+	cat >expect <<-EOF &&
+	pair.one foo
+	pair.two bar
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'git config ignores pairs without count' '
+	test_must_fail env GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
+		git config pair.one 2>error &&
+	test_must_be_empty error
+'
+
+test_expect_success 'git config ignores pairs with zero count' '
+	test_must_fail env \
+		GIT_CONFIG_COUNT=0 \
+		GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
+		git config pair.one
+'
+
+test_expect_success 'git config ignores pairs exceeding count' '
+	GIT_CONFIG_COUNT=1 \
+		GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
+		GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
+		git config --get-regexp "pair.*" >actual &&
+	cat >expect <<-EOF &&
+	pair.one value
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'git config ignores pairs with zero count' '
+	test_must_fail env \
+		GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
+		git config pair.one >error &&
+	test_must_be_empty error
+'
+
+test_expect_success 'git config ignores pairs with empty count' '
+	test_must_fail env \
+		GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
+		git config pair.one >error &&
+	test_must_be_empty error
+'
+
+test_expect_success 'git config fails with invalid count' '
+	test_must_fail env GIT_CONFIG_COUNT=10a git config --list 2>error &&
+	test_i18ngrep "bogus count" error &&
+	test_must_fail env GIT_CONFIG_COUNT=9999999999999999 git config --list 2>error &&
+	test_i18ngrep "too many entries" error
+'
+
+test_expect_success 'git config fails with missing config key' '
+	test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_VALUE_0="value" \
+		git config --list 2>error &&
+	test_i18ngrep "missing config key" error
+'
+
+test_expect_success 'git config fails with missing config value' '
+	test_must_fail env GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="pair.one" \
+		git config --list 2>error &&
+	test_i18ngrep "missing config value" error
+'
+
+test_expect_success 'git config fails with invalid config pair key' '
+	test_must_fail env GIT_CONFIG_COUNT=1 \
+		GIT_CONFIG_KEY_0= GIT_CONFIG_VALUE_0=value \
+		git config --list &&
+	test_must_fail env GIT_CONFIG_COUNT=1 \
+		GIT_CONFIG_KEY_0=missing-section GIT_CONFIG_VALUE_0=value \
+		git config --list
+'
+
+test_expect_success 'environment overrides config file' '
+	test_when_finished "rm -f .git/config" &&
+	cat >.git/config <<-EOF &&
+	[pair]
+	one = value
+	EOF
+	GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=override \
+		git config pair.one >actual &&
+	cat >expect <<-EOF &&
+	override
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'GIT_CONFIG_PARAMETERS overrides environment config' '
+	GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
+		GIT_CONFIG_PARAMETERS="${SQ}pair.one=override${SQ}" \
+		git config pair.one >actual &&
+	cat >expect <<-EOF &&
+	override
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'command line overrides environment config' '
+	GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=pair.one GIT_CONFIG_VALUE_0=value \
+		git -c pair.one=override config pair.one >actual &&
+	cat >expect <<-EOF &&
+	override
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'git config --edit works' '
 	git config -f tmp test.value no &&
 	echo test.value=yes >expect &&
@@ -1653,9 +1882,11 @@
 	file:.git/config	user.override=local
 	file:.git/config	include.path=../include/relative.include
 	file:.git/../include/relative.include	user.relative=include
+	command line:	user.environ=true
 	command line:	user.cmdline=true
 	EOF
-	git -c user.cmdline=true config --list --show-origin >output &&
+	GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.environ GIT_CONFIG_VALUE_0=true\
+		git -c user.cmdline=true config --list --show-origin >output &&
 	test_cmp expect output
 '
 
@@ -1761,11 +1992,11 @@
 
 test_expect_success '--show-origin blob ref' '
 	cat >expect <<-\EOF &&
-	blob:master:custom.conf	user.custom=true
+	blob:main:custom.conf	user.custom=true
 	EOF
 	git add "$CUSTOM_CONFIG_FILE" &&
 	git commit -m "new config file" &&
-	git config --blob=master:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
+	git config --blob=main:"$CUSTOM_CONFIG_FILE" --show-origin --list >output &&
 	test_cmp expect output
 '
 
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 2dc853d..ac947bf 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -5,6 +5,9 @@
 
 test_description='Test shared repository initialization'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Remove a default ACL from the test dir if possible.
@@ -115,13 +118,13 @@
 	umask 077 &&
 	git config core.sharedRepository group &&
 	git reflog expire --all &&
-	actual="$(ls -l .git/logs/refs/heads/master)" &&
+	actual="$(ls -l .git/logs/refs/heads/main)" &&
 	case "$actual" in
 	-rw-rw-*)
 		: happy
 		;;
 	*)
-		echo Ooops, .git/logs/refs/heads/master is not 0662 [$actual]
+		echo Ooops, .git/logs/refs/heads/main is not 0662 [$actual]
 		false
 		;;
 	esac
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index f1e1b28..ccbb116 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -312,7 +312,7 @@
 test_expect_success 'conditional include, onbranch' '
 	echo "[includeIf \"onbranch:foo-branch\"]path=bar9" >>.git/config &&
 	echo "[test]nine=9" >.git/bar9 &&
-	git checkout -b master &&
+	git checkout -b main &&
 	test_must_fail git config test.nine &&
 	git checkout -b foo-branch &&
 	echo 9 >expect &&
@@ -352,9 +352,7 @@
 	git init --bare cycle &&
 	git -C cycle config include.path cycle &&
 	git config -f cycle/cycle include.path config &&
-	test_must_fail \
-		env GIT_TEST_GETTEXT_POISON=false \
-		git -C cycle config --get-all test.value 2>stderr &&
+	test_must_fail git -C cycle config --get-all test.value 2>stderr &&
 	grep "exceeded maximum include depth" stderr
 '
 
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index 3a527e3..88b119a 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -208,14 +208,14 @@
 	echo "[" >>.git/config &&
 	echo "fatal: bad config line 34 in file .git/config" >expect &&
 	test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'proper error on error in custom config files' '
 	echo "[" >>syntax-error &&
 	echo "fatal: bad config line 1 in file syntax-error" >expect &&
 	test_expect_code 128 test-tool config configset_get_value foo.bar syntax-error 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'check line errors for malformed values' '
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 6ee7d21..e31f65f 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test git update-ref and basic ref logging'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 Z=$ZERO_OID
@@ -371,7 +374,7 @@
 	echo "$C" >expect &&
 	test_cmp expect o &&
 	echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-	test_i18ncmp expect e
+	test_cmp expect e
 '
 test_expect_success 'Query main@{2005-05-25} (before history)' '
 	test_when_finished "rm -f o e" &&
@@ -379,7 +382,7 @@
 	echo "$C" >expect &&
 	test_cmp expect o &&
 	echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-	test_i18ncmp expect e
+	test_cmp expect e
 '
 test_expect_success 'Query "main@{May 26 2005 23:31:59}" (1 second before history)' '
 	test_when_finished "rm -f o e" &&
@@ -387,7 +390,7 @@
 	echo "$C" >expect &&
 	test_cmp expect o &&
 	echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-	test_i18ncmp expect e
+	test_cmp expect e
 '
 test_expect_success 'Query "main@{May 26 2005 23:32:00}" (exactly history start)' '
 	test_when_finished "rm -f o e" &&
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 98e4a86..cabc516 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -134,18 +134,18 @@
 test_expect_success "check-ref-format --branch @{-1}" '
 	T=$(git write-tree) &&
 	sha1=$(echo A | git commit-tree $T) &&
-	git update-ref refs/heads/master $sha1 &&
-	git update-ref refs/remotes/origin/master $sha1 &&
-	git checkout master &&
-	git checkout origin/master &&
-	git checkout master &&
+	git update-ref refs/heads/main $sha1 &&
+	git update-ref refs/remotes/origin/main $sha1 &&
+	git checkout main &&
+	git checkout origin/main &&
+	git checkout main &&
 	refname=$(git check-ref-format --branch @{-1}) &&
 	test "$refname" = "$sha1" &&
 	refname2=$(git check-ref-format --branch @{-2}) &&
-	test "$refname2" = master'
+	test "$refname2" = main'
 
-test_expect_success 'check-ref-format --branch -naster' '
-	test_must_fail git check-ref-format --branch -naster >actual &&
+test_expect_success 'check-ref-format --branch -nain' '
+	test_must_fail git check-ref-format --branch -nain >actual &&
 	test_must_be_empty actual
 '
 
@@ -154,11 +154,11 @@
 
 	T=$(git write-tree) &&
 	sha1=$(echo A | git commit-tree $T) &&
-	git update-ref refs/heads/master $sha1 &&
-	git update-ref refs/remotes/origin/master $sha1 &&
-	git checkout master &&
-	git checkout origin/master &&
-	git checkout master &&
+	git update-ref refs/heads/main $sha1 &&
+	git update-ref refs/remotes/origin/main $sha1 &&
+	git checkout main &&
+	git checkout origin/main &&
+	git checkout main &&
 	refname=$(
 		cd subdir &&
 		git check-ref-format --branch @{-1}
@@ -171,9 +171,9 @@
 	test_must_be_empty actual
 '
 
-test_expect_success 'check-ref-format --branch master from non-repo' '
-	echo master >expect &&
-	nongit git check-ref-format --branch master >actual &&
+test_expect_success 'check-ref-format --branch main from non-repo' '
+	echo main >expect &&
+	nongit git check-ref-format --branch main >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 5d955c3..6ce62f8 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='show-ref'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -9,7 +12,7 @@
 	git checkout -b side &&
 	test_commit B &&
 	git tag -f -a -m "annotated B" B &&
-	git checkout master &&
+	git checkout main &&
 	test_commit C &&
 	git branch B A^0
 '
@@ -92,23 +95,23 @@
 	git show-ref --verify -d refs/tags/A refs/tags/C >actual &&
 	test_cmp expect actual &&
 
-	echo $(git rev-parse refs/heads/master) refs/heads/master >expect &&
-	git show-ref -d master >actual &&
+	echo $(git rev-parse refs/heads/main) refs/heads/main >expect &&
+	git show-ref -d main >actual &&
 	test_cmp expect actual &&
 
-	git show-ref -d heads/master >actual &&
+	git show-ref -d heads/main >actual &&
 	test_cmp expect actual &&
 
-	git show-ref -d refs/heads/master >actual &&
+	git show-ref -d refs/heads/main >actual &&
 	test_cmp expect actual &&
 
-	git show-ref -d --verify refs/heads/master >actual &&
+	git show-ref -d --verify refs/heads/main >actual &&
 	test_cmp expect actual &&
 
-	test_must_fail git show-ref -d --verify master >actual &&
+	test_must_fail git show-ref -d --verify main >actual &&
 	test_must_be_empty actual &&
 
-	test_must_fail git show-ref -d --verify heads/master >actual &&
+	test_must_fail git show-ref -d --verify heads/main >actual &&
 	test_must_be_empty actual &&
 
 	test_must_fail git show-ref --verify -d A C >actual &&
@@ -120,7 +123,7 @@
 '
 
 test_expect_success 'show-ref --heads, --tags, --head, pattern' '
-	for branch in B master side
+	for branch in B main side
 	do
 		echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
 	done >expect.heads &&
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index 2d142e5..8b51c4e 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -101,7 +101,7 @@
 		printf "%s\n" "delete $delname" "create $addname $D"
 	fi >commands &&
 	test_must_fail git update-ref --stdin <commands 2>output.err &&
-	test_i18ncmp expected-err output.err &&
+	test_cmp expected-err output.err &&
 	printf "%s\n" "$C $delref" >expected-refs &&
 	git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
 	test_cmp expected-refs actual-refs
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index 74af927..a237d98 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -2,6 +2,9 @@
 
 test_description='test main ref store api'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 RUN="test-tool ref-store main"
@@ -14,24 +17,18 @@
 	N=`find .git/refs -type f | wc -l`
 '
 
-test_expect_success 'peel_ref(new-tag)' '
-	git rev-parse HEAD >expected &&
-	git tag -a -m new-tag new-tag HEAD &&
-	$RUN peel-ref refs/tags/new-tag >actual &&
-	test_cmp expected actual
-'
-
-test_expect_success 'create_symref(FOO, refs/heads/master)' '
-	$RUN create-symref FOO refs/heads/master nothing &&
-	echo refs/heads/master >expected &&
+test_expect_success 'create_symref(FOO, refs/heads/main)' '
+	$RUN create-symref FOO refs/heads/main nothing &&
+	echo refs/heads/main >expected &&
 	git symbolic-ref FOO >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
+	git tag -a -m new-tag new-tag HEAD &&
 	git rev-parse FOO -- &&
 	git rev-parse refs/tags/new-tag -- &&
-	m=$(git rev-parse master) &&
+	m=$(git rev-parse main) &&
 	REF_NO_DEREF=1 &&
 	$RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
 	test_must_fail git rev-parse --symbolic-full-name FOO &&
@@ -39,19 +36,19 @@
 	test_must_fail git rev-parse refs/tags/new-tag --
 '
 
-test_expect_success 'rename_refs(master, new-master)' '
-	git rev-parse master >expected &&
-	$RUN rename-ref refs/heads/master refs/heads/new-master &&
-	git rev-parse new-master >actual &&
+test_expect_success 'rename_refs(main, new-main)' '
+	git rev-parse main >expected &&
+	$RUN rename-ref refs/heads/main refs/heads/new-main &&
+	git rev-parse new-main >actual &&
 	test_cmp expected actual &&
-	test_commit recreate-master
+	test_commit recreate-main
 '
 
 test_expect_success 'for_each_ref(refs/heads/)' '
 	$RUN for-each-ref refs/heads/ | cut -d" " -f 2- >actual &&
 	cat >expected <<-\EOF &&
-	master 0x0
-	new-master 0x0
+	main 0x0
+	new-main 0x0
 	EOF
 	test_cmp expected actual
 '
@@ -62,23 +59,23 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'resolve_ref(new-master)' '
-	SHA1=`git rev-parse new-master` &&
-	echo "$SHA1 refs/heads/new-master 0x0" >expected &&
-	$RUN resolve-ref refs/heads/new-master 0 >actual &&
+test_expect_success 'resolve_ref(new-main)' '
+	SHA1=`git rev-parse new-main` &&
+	echo "$SHA1 refs/heads/new-main 0x0" >expected &&
+	$RUN resolve-ref refs/heads/new-main 0 >actual &&
 	test_cmp expected actual
 '
 
-test_expect_success 'verify_ref(new-master)' '
-	$RUN verify-ref refs/heads/new-master
+test_expect_success 'verify_ref(new-main)' '
+	$RUN verify-ref refs/heads/new-main
 '
 
 test_expect_success 'for_each_reflog()' '
 	$RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual &&
 	cat >expected <<-\EOF &&
 	HEAD 0x1
-	refs/heads/master 0x0
-	refs/heads/new-master 0x0
+	refs/heads/main 0x0
+	refs/heads/new-main 0x0
 	EOF
 	test_cmp expected actual
 '
@@ -86,12 +83,12 @@
 test_expect_success 'for_each_reflog_ent()' '
 	$RUN for-each-reflog-ent HEAD >actual &&
 	head -n1 actual | grep one &&
-	tail -n2 actual | head -n1 | grep recreate-master
+	tail -n2 actual | head -n1 | grep recreate-main
 '
 
 test_expect_success 'for_each_reflog_ent_reverse()' '
 	$RUN for-each-reflog-ent-reverse HEAD >actual &&
-	head -n1 actual | grep recreate-master &&
+	head -n1 actual | grep recreate-main &&
 	tail -n2 actual | head -n1 | grep one
 '
 
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index 36b7ef5..0a87058 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -2,6 +2,9 @@
 
 test_description='test submodule ref store api'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 RUN="test-tool ref-store submodule:sub"
@@ -11,7 +14,8 @@
 	(
 		cd sub &&
 		test_commit first &&
-		git checkout -b new-master
+		git checkout -b new-main &&
+		git tag -a -m new-tag new-tag HEAD
 	)
 '
 
@@ -19,15 +23,8 @@
 	test_must_fail $RUN pack-refs 3
 '
 
-test_expect_success 'peel_ref(new-tag)' '
-	git -C sub rev-parse HEAD >expected &&
-	git -C sub tag -a -m new-tag new-tag HEAD &&
-	$RUN peel-ref refs/tags/new-tag >actual &&
-	test_cmp expected actual
-'
-
 test_expect_success 'create_symref() not allowed' '
-	test_must_fail $RUN create-symref FOO refs/heads/master nothing
+	test_must_fail $RUN create-symref FOO refs/heads/main nothing
 '
 
 test_expect_success 'delete_refs() not allowed' '
@@ -35,14 +32,14 @@
 '
 
 test_expect_success 'rename_refs() not allowed' '
-	test_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master
+	test_must_fail $RUN rename-ref refs/heads/main refs/heads/new-main
 '
 
 test_expect_success 'for_each_ref(refs/heads/)' '
 	$RUN for-each-ref refs/heads/ | cut -d" " -f 2- >actual &&
 	cat >expected <<-\EOF &&
-	master 0x0
-	new-master 0x0
+	main 0x0
+	new-main 0x0
 	EOF
 	test_cmp expected actual
 '
@@ -53,23 +50,23 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'resolve_ref(master)' '
-	SHA1=`git -C sub rev-parse master` &&
-	echo "$SHA1 refs/heads/master 0x0" >expected &&
-	$RUN resolve-ref refs/heads/master 0 >actual &&
+test_expect_success 'resolve_ref(main)' '
+	SHA1=`git -C sub rev-parse main` &&
+	echo "$SHA1 refs/heads/main 0x0" >expected &&
+	$RUN resolve-ref refs/heads/main 0 >actual &&
 	test_cmp expected actual
 '
 
-test_expect_success 'verify_ref(new-master)' '
-	$RUN verify-ref refs/heads/new-master
+test_expect_success 'verify_ref(new-main)' '
+	$RUN verify-ref refs/heads/new-main
 '
 
 test_expect_success 'for_each_reflog()' '
 	$RUN for-each-reflog | sort | cut -d" " -f 2- >actual &&
 	cat >expected <<-\EOF &&
 	HEAD 0x1
-	refs/heads/master 0x0
-	refs/heads/new-master 0x0
+	refs/heads/main 0x0
+	refs/heads/new-main 0x0
 	EOF
 	test_cmp expected actual
 '
@@ -77,12 +74,12 @@
 test_expect_success 'for_each_reflog_ent()' '
 	$RUN for-each-reflog-ent HEAD >actual &&
 	head -n1 actual | grep first &&
-	tail -n2 actual | head -n1 | grep master.to.new
+	tail -n2 actual | head -n1 | grep main.to.new
 '
 
 test_expect_success 'for_each_reflog_ent_reverse()' '
 	$RUN for-each-reflog-ent-reverse HEAD >actual &&
-	head -n1 actual | grep master.to.new &&
+	head -n1 actual | grep main.to.new &&
 	tail -n2 actual | head -n1 | grep first
 '
 
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
index 9a84858..d3fe777 100755
--- a/t/t1407-worktree-ref-store.sh
+++ b/t/t1407-worktree-ref-store.sh
@@ -2,6 +2,9 @@
 
 test_description='test worktree ref store api'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 RWT="test-tool ref-store worktree:wt"
@@ -9,7 +12,7 @@
 
 test_expect_success 'setup' '
 	test_commit first &&
-	git worktree add -b wt-master wt &&
+	git worktree add -b wt-main wt &&
 	(
 		cd wt &&
 		test_commit second
@@ -17,34 +20,34 @@
 '
 
 test_expect_success 'resolve_ref(<shared-ref>)' '
-	SHA1=`git rev-parse master` &&
-	echo "$SHA1 refs/heads/master 0x0" >expected &&
-	$RWT resolve-ref refs/heads/master 0 >actual &&
+	SHA1=`git rev-parse main` &&
+	echo "$SHA1 refs/heads/main 0x0" >expected &&
+	$RWT resolve-ref refs/heads/main 0 >actual &&
 	test_cmp expected actual &&
-	$RMAIN resolve-ref refs/heads/master 0 >actual &&
+	$RMAIN resolve-ref refs/heads/main 0 >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'resolve_ref(<per-worktree-ref>)' '
 	SHA1=`git -C wt rev-parse HEAD` &&
-	echo "$SHA1 refs/heads/wt-master 0x1" >expected &&
+	echo "$SHA1 refs/heads/wt-main 0x1" >expected &&
 	$RWT resolve-ref HEAD 0 >actual &&
 	test_cmp expected actual &&
 
 	SHA1=`git rev-parse HEAD` &&
-	echo "$SHA1 refs/heads/master 0x1" >expected &&
+	echo "$SHA1 refs/heads/main 0x1" >expected &&
 	$RMAIN resolve-ref HEAD 0 >actual &&
 	test_cmp expected actual
 '
 
-test_expect_success 'create_symref(FOO, refs/heads/master)' '
-	$RWT create-symref FOO refs/heads/master nothing &&
-	echo refs/heads/master >expected &&
+test_expect_success 'create_symref(FOO, refs/heads/main)' '
+	$RWT create-symref FOO refs/heads/main nothing &&
+	echo refs/heads/main >expected &&
 	git -C wt symbolic-ref FOO >actual &&
 	test_cmp expected actual &&
 
-	$RMAIN create-symref FOO refs/heads/wt-master nothing &&
-	echo refs/heads/wt-master >expected &&
+	$RMAIN create-symref FOO refs/heads/wt-main nothing &&
+	echo refs/heads/wt-main >expected &&
 	git symbolic-ref FOO >actual &&
 	test_cmp expected actual
 '
@@ -63,8 +66,8 @@
 	HEAD 0x1
 	PSEUDO-WT 0x0
 	refs/bisect/wt-random 0x0
-	refs/heads/master 0x0
-	refs/heads/wt-master 0x0
+	refs/heads/main 0x0
+	refs/heads/wt-main 0x0
 	EOF
 	test_cmp expected actual &&
 
@@ -73,8 +76,8 @@
 	HEAD 0x1
 	PSEUDO-MAIN 0x0
 	refs/bisect/random 0x0
-	refs/heads/master 0x0
-	refs/heads/wt-master 0x0
+	refs/heads/main 0x0
+	refs/heads/wt-main 0x0
 	EOF
 	test_cmp expected actual
 '
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
index 1e44a17..41ba1f1 100755
--- a/t/t1408-packed-refs.sh
+++ b/t/t1408-packed-refs.sh
@@ -2,6 +2,9 @@
 
 test_description='packed-refs entries are covered by loose refs'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -9,32 +12,32 @@
 	git commit --allow-empty -m one &&
 	one=$(git rev-parse HEAD) &&
 	git for-each-ref >actual &&
-	echo "$one commit	refs/heads/master" >expect &&
+	echo "$one commit	refs/heads/main" >expect &&
 	test_cmp expect actual &&
 
 	git pack-refs --all &&
 	git for-each-ref >actual &&
-	echo "$one commit	refs/heads/master" >expect &&
+	echo "$one commit	refs/heads/main" >expect &&
 	test_cmp expect actual &&
 
 	git checkout --orphan another &&
 	test_tick &&
 	git commit --allow-empty -m two &&
 	two=$(git rev-parse HEAD) &&
-	git checkout -B master &&
+	git checkout -B main &&
 	git branch -D another &&
 
 	git for-each-ref >actual &&
-	echo "$two commit	refs/heads/master" >expect &&
+	echo "$two commit	refs/heads/main" >expect &&
 	test_cmp expect actual &&
 
 	git reflog expire --expire=now --all &&
 	git prune &&
-	git tag -m v1.0 v1.0 master
+	git tag -m v1.0 v1.0 main
 '
 
 test_expect_success 'no error from stale entry in packed-refs' '
-	git describe master >actual 2>&1 &&
+	git describe main >actual 2>&1 &&
 	echo "v1.0" >expect &&
 	test_cmp expect actual
 '
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 730a43d..27b9080 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test prune and reflog expiration'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_have () {
@@ -99,7 +102,7 @@
 
 	check_fsck &&
 
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 4 output
 '
 
@@ -116,7 +119,7 @@
 
 	check_have A B C D E F G H I J K L &&
 
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 5 output
 '
 
@@ -135,7 +138,7 @@
 		--stale-fix \
 		--all &&
 
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 5 output &&
 
 	check_fsck "missing blob $F"
@@ -149,12 +152,38 @@
 		--stale-fix \
 		--all &&
 
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 2 output &&
 
 	check_fsck "dangling commit $K"
 '
 
+test_expect_success '--stale-fix handles missing objects generously' '
+	git -c core.logAllRefUpdates=false fast-import --date-format=now <<-EOS &&
+	commit refs/heads/stale-fix
+	mark :1
+	committer Author <a@uth.or> now
+	data <<EOF
+	start stale fix
+	EOF
+	M 100644 inline file
+	data <<EOF
+	contents
+	EOF
+	commit refs/heads/stale-fix
+	committer Author <a@uth.or> now
+	data <<EOF
+	stale fix branch tip
+	EOF
+	from :1
+	EOS
+
+	parent_oid=$(git rev-parse stale-fix^) &&
+	test_when_finished "recover $parent_oid" &&
+	corrupt $parent_oid &&
+	git reflog expire --stale-fix
+'
+
 test_expect_success 'prune and fsck' '
 
 	git prune &&
@@ -186,29 +215,29 @@
 	git commit -m tiger C &&
 
 	HEAD_entry_count=$(git reflog | wc -l) &&
-	master_entry_count=$(git reflog show master | wc -l) &&
+	main_entry_count=$(git reflog show main | wc -l) &&
 
 	test $HEAD_entry_count = 5 &&
-	test $master_entry_count = 5 &&
+	test $main_entry_count = 5 &&
 
 
-	git reflog delete master@{1} &&
-	git reflog show master > output &&
-	test_line_count = $(($master_entry_count - 1)) output &&
+	git reflog delete main@{1} &&
+	git reflog show main > output &&
+	test_line_count = $(($main_entry_count - 1)) output &&
 	test $HEAD_entry_count = $(git reflog | wc -l) &&
 	! grep ox < output &&
 
-	master_entry_count=$(wc -l < output) &&
+	main_entry_count=$(wc -l < output) &&
 
 	git reflog delete HEAD@{1} &&
 	test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
-	test $master_entry_count = $(git reflog show master | wc -l) &&
+	test $main_entry_count = $(git reflog show main | wc -l) &&
 
 	HEAD_entry_count=$(git reflog | wc -l) &&
 
-	git reflog delete master@{07.04.2005.15:15:00.-0700} &&
-	git reflog show master > output &&
-	test_line_count = $(($master_entry_count - 1)) output &&
+	git reflog delete main@{07.04.2005.15:15:00.-0700} &&
+	git reflog show main > output &&
+	test_line_count = $(($main_entry_count - 1)) output &&
 	! grep dragon < output
 
 '
@@ -216,7 +245,7 @@
 test_expect_success 'rewind2' '
 
 	test_tick && git reset --hard HEAD~2 &&
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 4 output
 '
 
@@ -226,7 +255,7 @@
 		--expire=never \
 		--expire-unreachable=never \
 		--all &&
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 4 output
 '
 
@@ -237,7 +266,7 @@
 	git reflog expire --verbose --all >output &&
 	test_line_count = 9 output &&
 
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 4 output
 '
 
@@ -246,7 +275,7 @@
 	test_config gc.reflogexpireunreachable false &&
 
 	git reflog expire --verbose --all &&
-	git reflog refs/heads/master >output &&
+	git reflog refs/heads/main >output &&
 	test_line_count = 4 output
 
 '
@@ -255,33 +284,33 @@
 	test_config gc.reflogexpire never &&
 	test_config gc.reflogexpireunreachable never &&
 
-	test_must_fail git reflog expire master@{123} 2>stderr &&
+	test_must_fail git reflog expire main@{123} 2>stderr &&
 	test_i18ngrep "points nowhere" stderr &&
 	test_must_fail git reflog expire does-not-exist 2>stderr &&
 	test_i18ngrep "points nowhere" stderr
 '
 
 test_expect_success 'checkout should not delete log for packed ref' '
-	test $(git reflog master | wc -l) = 4 &&
+	test $(git reflog main | wc -l) = 4 &&
 	git branch foo &&
 	git pack-refs --all &&
 	git checkout foo &&
-	test $(git reflog master | wc -l) = 4
+	test $(git reflog main | wc -l) = 4
 '
 
 test_expect_success 'stale dirs do not cause d/f conflicts (reflogs on)' '
 	test_when_finished "git branch -d one || git branch -d one/two" &&
 
-	git branch one/two master &&
-	echo "one/two@{0} branch: Created from master" >expect &&
+	git branch one/two main &&
+	echo "one/two@{0} branch: Created from main" >expect &&
 	git log -g --format="%gd %gs" one/two >actual &&
 	test_cmp expect actual &&
 	git branch -d one/two &&
 
 	# now logs/refs/heads/one is a stale directory, but
 	# we should move it out of the way to create "one" reflog
-	git branch one master &&
-	echo "one@{0} branch: Created from master" >expect &&
+	git branch one main &&
+	echo "one@{0} branch: Created from main" >expect &&
 	git log -g --format="%gd %gs" one >actual &&
 	test_cmp expect actual
 '
@@ -289,15 +318,15 @@
 test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
 	test_when_finished "git branch -d one || git branch -d one/two" &&
 
-	git branch one/two master &&
-	echo "one/two@{0} branch: Created from master" >expect &&
+	git branch one/two main &&
+	echo "one/two@{0} branch: Created from main" >expect &&
 	git log -g --format="%gd %gs" one/two >actual &&
 	test_cmp expect actual &&
 	git branch -d one/two &&
 
 	# same as before, but we only create a reflog for "one" if
 	# it already exists, which it does not
-	git -c core.logallrefupdates=false branch one master &&
+	git -c core.logallrefupdates=false branch one main &&
 	git log -g --format="%gd %gs" one >actual &&
 	test_must_be_empty actual
 '
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 985daf1..0bb319b 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Test reflog display routines'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -167,7 +170,7 @@
 '
 
 test_expect_success 'reflog exists works' '
-	git reflog exists refs/heads/master &&
+	git reflog exists refs/heads/main &&
 	! git reflog exists refs/heads/nonexistent
 '
 
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 3acd895..977603f 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -4,11 +4,8 @@
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
-	test_tick &&
-	echo content >file && git add file && git commit -m one &&
-	git tag one &&
-	echo content >>file && git add file && git commit -m two &&
-	git tag two
+	test_commit one file content &&
+	test_commit --append two file content
 '
 
 test_expect_success 'setup reflog with alternating commits' '
diff --git a/t/t1413-reflog-detach.sh b/t/t1413-reflog-detach.sh
index c730600..bde0520 100755
--- a/t/t1413-reflog-detach.sh
+++ b/t/t1413-reflog-detach.sh
@@ -1,10 +1,13 @@
 #!/bin/sh
 
 test_description='Test reflog interaction with detached HEAD'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 reset_state () {
-	git checkout master &&
+	git checkout main &&
 	cp saved_reflog .git/logs/HEAD
 }
 
@@ -19,14 +22,14 @@
 
 test_expect_success baseline '
 	reset_state &&
-	git rev-parse master master^ >expect &&
+	git rev-parse main main^ >expect &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'switch to branch' '
 	reset_state &&
-	git rev-parse side master master^ >expect &&
+	git rev-parse side main main^ >expect &&
 	git checkout side &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
@@ -34,34 +37,34 @@
 
 test_expect_success 'detach to other' '
 	reset_state &&
-	git rev-parse master side master master^ >expect &&
+	git rev-parse main side main main^ >expect &&
 	git checkout side &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'detach to self' '
 	reset_state &&
-	git rev-parse master master master^ >expect &&
-	git checkout master^0 &&
+	git rev-parse main main main^ >expect &&
+	git checkout main^0 &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'attach to self' '
 	reset_state &&
-	git rev-parse master master master master^ >expect &&
-	git checkout master^0 &&
-	git checkout master &&
+	git rev-parse main main main main^ >expect &&
+	git checkout main^0 &&
+	git checkout main &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'attach to other' '
 	reset_state &&
-	git rev-parse side master master master^ >expect &&
-	git checkout master^0 &&
+	git rev-parse side main main main^ >expect &&
+	git checkout main^0 &&
 	git checkout side &&
 	git log -g --format=%H >actual &&
 	test_cmp expect actual
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
index 1181a9f..80d9470 100755
--- a/t/t1414-reflog-walk.sh
+++ b/t/t1414-reflog-walk.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='various tests of reflog walk (log -g) behavior'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'set up some reflog entries' '
@@ -8,7 +11,7 @@
 	test_commit two &&
 	git checkout -b side HEAD^ &&
 	test_commit three &&
-	git merge --no-commit master &&
+	git merge --no-commit main &&
 	echo evil-merge-content >>one.t &&
 	test_tick &&
 	git commit --no-edit -a
@@ -20,9 +23,9 @@
 
 test_expect_success 'set up expected reflog' '
 	cat >expect.all <<-EOF
-	HEAD@{0} commit (merge): Merge branch ${SQ}master${SQ} into side
+	HEAD@{0} commit (merge): Merge branch ${SQ}main${SQ} into side
 	HEAD@{1} commit: three
-	HEAD@{2} checkout: moving from master to side
+	HEAD@{2} checkout: moving from main to side
 	HEAD@{3} commit: two
 	HEAD@{4} commit (initial): one
 	EOF
@@ -73,15 +76,15 @@
 	#      sort ignores the bits after the timestamp.
 	#
 	#   2. POSIX leaves undefined whether this is a stable sort or not. So
-	#      we use "-k 1" to ensure that we see HEAD before master before
+	#      we use "-k 1" to ensure that we see HEAD before main before
 	#      side when breaking ties.
 	{
 		do_walk --date=unix HEAD &&
 		do_walk --date=unix side &&
-		do_walk --date=unix master
+		do_walk --date=unix main
 	} >expect.raw &&
 	sort -t "{" -k 2nr -k 1 <expect.raw >expect &&
-	do_walk --date=unix HEAD master side >actual &&
+	do_walk --date=unix HEAD main side >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh
index f6e741c..6c94102 100755
--- a/t/t1416-ref-transaction-hooks.sh
+++ b/t/t1416-ref-transaction-hooks.sh
@@ -2,6 +2,9 @@
 
 test_description='reference transaction hooks'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -53,11 +56,11 @@
 	EOF
 	cat >expect <<-EOF &&
 		$ZERO_OID $POST_OID HEAD
-		$ZERO_OID $POST_OID refs/heads/master
+		$ZERO_OID $POST_OID refs/heads/main
 	EOF
 	git update-ref HEAD POST <<-EOF &&
 		update HEAD $ZERO_OID $POST_OID
-		update refs/heads/master $ZERO_OID $POST_OID
+		update refs/heads/main $ZERO_OID $POST_OID
 	EOF
 	test_cmp expect actual
 '
@@ -76,7 +79,7 @@
 	EOF
 	cat >expect <<-EOF &&
 		$ZERO_OID $POST_OID HEAD
-		$ZERO_OID $POST_OID refs/heads/master
+		$ZERO_OID $POST_OID refs/heads/main
 	EOF
 	git update-ref HEAD POST &&
 	test_cmp expect actual
@@ -96,12 +99,12 @@
 	EOF
 	cat >expect <<-EOF &&
 		$ZERO_OID $POST_OID HEAD
-		$ZERO_OID $POST_OID refs/heads/master
+		$ZERO_OID $POST_OID refs/heads/main
 	EOF
 	git update-ref --stdin <<-EOF &&
 		start
 		update HEAD POST $ZERO_OID
-		update refs/heads/master POST $ZERO_OID
+		update refs/heads/main POST $ZERO_OID
 		abort
 	EOF
 	test_cmp expect actual
diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh
index c7878a6..b1839e0 100755
--- a/t/t1430-bad-ref-name.sh
+++ b/t/t1430-bad-ref-name.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Test handling of ref names that check-ref-format rejects'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -17,7 +20,7 @@
 		corrupt
 		COMMIT
 
-		from refs/heads/master
+		from refs/heads/main
 
 	INPUT_END
 	test_must_fail git fast-import <input
@@ -32,14 +35,14 @@
 		corrupt
 		COMMIT
 
-		from refs/heads/master
+		from refs/heads/main
 
 	INPUT_END
 	test_must_fail git fast-import <input
 '
 
 test_expect_success 'git branch shows badly named ref as warning' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch >output 2>error &&
 	test_i18ngrep -e "ignoring ref with broken name refs/heads/broken\.\.\.ref" error &&
@@ -47,7 +50,7 @@
 '
 
 test_expect_success 'branch -d can delete badly named ref' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch -d broken...ref &&
 	git branch >output 2>error &&
@@ -56,7 +59,7 @@
 '
 
 test_expect_success 'branch -D can delete badly named ref' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch -D broken...ref &&
 	git branch >output 2>error &&
@@ -98,17 +101,17 @@
 	test_might_fail git branch -D goodref &&
 	git branch goodref &&
 	test_must_fail git branch -m goodref broken...ref &&
-	test_cmp_rev master goodref &&
+	test_cmp_rev main goodref &&
 	git branch >output 2>error &&
 	! grep -e "broken\.\.\.ref" error &&
 	! grep -e "broken\.\.\.ref" output
 '
 
 test_expect_failure 'branch -m can rename from a bad ref name' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch -m broken...ref renamed &&
-	test_cmp_rev master renamed &&
+	test_cmp_rev main renamed &&
 	git branch >output 2>error &&
 	! grep -e "broken\.\.\.ref" error &&
 	! grep -e "broken\.\.\.ref" output
@@ -122,7 +125,7 @@
 	! grep -e "broken\.\.\.ref" output
 '
 
-test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' '
+test_expect_failure 'push --mirror can delete badly named ref' '
 	top=$(pwd) &&
 	git init src &&
 	git init dest &&
@@ -135,7 +138,7 @@
 		cd dest &&
 		test_commit two &&
 		git checkout --detach &&
-		cp .git/refs/heads/master .git/refs/heads/broken...ref
+		cp .git/refs/heads/main .git/refs/heads/broken...ref
 	) &&
 	git -C src push --mirror "file://$top/dest" &&
 	git -C dest branch >output 2>error &&
@@ -146,7 +149,7 @@
 test_expect_success 'rev-parse skips symref pointing to broken name' '
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch shadow one &&
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	printf "ref: refs/heads/broken...ref\n" >.git/refs/tags/shadow &&
 	test_when_finished "rm -f .git/refs/tags/shadow" &&
 	git rev-parse --verify one >expect &&
@@ -156,11 +159,11 @@
 '
 
 test_expect_success 'for-each-ref emits warnings for broken names' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
 	test_when_finished "rm -f .git/refs/heads/badname" &&
-	printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
 	test_when_finished "rm -f .git/refs/heads/broken...symref" &&
 	git for-each-ref >output 2>error &&
 	! grep -e "broken\.\.\.ref" output &&
@@ -172,7 +175,7 @@
 '
 
 test_expect_success 'update-ref -d can delete broken name' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git update-ref -d refs/heads/broken...ref >output 2>error &&
 	test_must_be_empty output &&
@@ -183,7 +186,7 @@
 '
 
 test_expect_success 'branch -d can delete broken name' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	git branch -d broken...ref >output 2>error &&
 	test_i18ngrep "Deleted branch broken...ref (was broken)" output &&
@@ -194,7 +197,7 @@
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref to broken name' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
 	test_when_finished "rm -f .git/refs/heads/badname" &&
@@ -205,7 +208,7 @@
 '
 
 test_expect_success 'branch -d can delete symref to broken name' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
 	test_when_finished "rm -f .git/refs/heads/badname" &&
@@ -234,7 +237,7 @@
 '
 
 test_expect_success 'update-ref -d can delete broken name through symref' '
-	cp .git/refs/heads/master .git/refs/heads/broken...ref &&
+	cp .git/refs/heads/main .git/refs/heads/broken...ref &&
 	test_when_finished "rm -f .git/refs/heads/broken...ref" &&
 	printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
 	test_when_finished "rm -f .git/refs/heads/badname" &&
@@ -245,7 +248,7 @@
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
-	printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
 	test_when_finished "rm -f .git/refs/heads/broken...symref" &&
 	git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
 	test_path_is_missing .git/refs/heads/broken...symref &&
@@ -254,11 +257,11 @@
 '
 
 test_expect_success 'branch -d can delete symref with broken name' '
-	printf "ref: refs/heads/master\n" >.git/refs/heads/broken...symref &&
+	printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
 	test_when_finished "rm -f .git/refs/heads/broken...symref" &&
 	git branch -d broken...symref >output 2>error &&
 	test_path_is_missing .git/refs/heads/broken...symref &&
-	test_i18ngrep "Deleted branch broken...symref (was refs/heads/master)" output &&
+	test_i18ngrep "Deleted branch broken...symref (was refs/heads/main)" output &&
 	test_must_be_empty error
 '
 
@@ -296,37 +299,37 @@
 '
 
 test_expect_success 'update-ref --stdin fails create with bad ref name' '
-	echo "create ~a refs/heads/master" >stdin &&
+	echo "create ~a refs/heads/main" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a" err
 '
 
 test_expect_success 'update-ref --stdin fails update with bad ref name' '
-	echo "update ~a refs/heads/master" >stdin &&
+	echo "update ~a refs/heads/main" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a" err
 '
 
 test_expect_success 'update-ref --stdin fails delete with bad ref name' '
-	echo "delete ~a refs/heads/master" >stdin &&
+	echo "delete ~a refs/heads/main" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a" err
 '
 
 test_expect_success 'update-ref --stdin -z fails create with bad ref name' '
-	printf "%s\0" "create ~a " refs/heads/master >stdin &&
+	printf "%s\0" "create ~a " refs/heads/main >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a " err
 '
 
 test_expect_success 'update-ref --stdin -z fails update with bad ref name' '
-	printf "%s\0" "update ~a" refs/heads/master "" >stdin &&
+	printf "%s\0" "update ~a" refs/heads/main "" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a" err
 '
 
 test_expect_success 'update-ref --stdin -z fails delete with bad ref name' '
-	printf "%s\0" "delete ~a" refs/heads/master >stdin &&
+	printf "%s\0" "delete ~a" refs/heads/main >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
 	grep "fatal: invalid ref format: ~a" err
 '
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index b17f5c2..5071ac6 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -3,7 +3,7 @@
 test_description='git fsck random collection of tests
 
 * (HEAD) B
-* (master) A
+* (main) A
 '
 
 . ./test-lib.sh
@@ -40,17 +40,13 @@
 # specific corruption you test afterwards, lest a later test trip over
 # it.
 
-test_expect_success 'setup: helpers for corruption tests' '
-	sha1_file() {
-		remainder=${1#??} &&
-		firsttwo=${1%$remainder} &&
-		echo ".git/objects/$firsttwo/$remainder"
-	} &&
+sha1_file () {
+	git rev-parse --git-path objects/$(test_oid_to_path "$1")
+}
 
-	remove_object() {
-		rm "$(sha1_file "$1")"
-	}
-'
+remove_object () {
+	rm "$(sha1_file "$1")"
+}
 
 test_expect_success 'object with bad sha1' '
 	sha=$(echo blob | git hash-object -w --stdin) &&
@@ -380,7 +376,7 @@
 	warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
 	warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
 	EOF
-	test_i18ncmp expect out
+	test_cmp expect out
 '
 
 test_expect_success 'tag with bad tagger' '
@@ -662,13 +658,15 @@
 	git init name-objects &&
 	(
 		cd name-objects &&
+		git config core.logAllRefUpdates false &&
 		test_commit julius caesar.t &&
-		test_commit augustus &&
-		test_commit caesar &&
+		test_commit augustus44 &&
+		test_commit caesar  &&
 		remove_object $(git rev-parse julius:caesar.t) &&
-		test_must_fail git fsck --name-objects >out &&
 		tree=$(git rev-parse --verify julius:) &&
-		test_i18ngrep "$tree (refs/tags/julius:" out
+		git tag -d julius &&
+		test_must_fail git fsck --name-objects >out &&
+		test_i18ngrep "$tree (refs/tags/augustus44\\^:" out
 	)
 '
 
@@ -806,7 +804,7 @@
 		git fsck >actual &&
 		# the output order is non-deterministic, as it comes from a hash
 		sort <actual >actual.sorted &&
-		test_i18ncmp expect actual.sorted
+		test_cmp expect actual.sorted
 	)
 '
 
@@ -816,7 +814,7 @@
 		git fsck --connectivity-only >actual &&
 		# the output order is non-deterministic, as it comes from a hash
 		sort <actual >actual.sorted &&
-		test_i18ncmp expect actual.sorted
+		test_cmp expect actual.sorted
 	)
 '
 
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 408b97d..deae916 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -1,8 +1,21 @@
 #!/bin/sh
 
 test_description='test git rev-parse'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
+test_one () {
+	dir="$1" &&
+	expect="$2" &&
+	shift &&
+	shift &&
+	echo "$expect" >expect &&
+	git -C "$dir" rev-parse "$@" >actual &&
+	test_cmp expect actual
+}
+
 # usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir absolute-git-dir
 test_rev_parse () {
 	d=
@@ -60,7 +73,13 @@
 
 test_expect_success 'setup' '
 	mkdir -p sub/dir work &&
-	cp -R .git repo.git
+	cp -R .git repo.git &&
+	git checkout -B main &&
+	test_commit abc &&
+	git checkout -b side &&
+	test_commit def &&
+	git checkout main &&
+	git worktree add worktree side
 '
 
 test_rev_parse toplevel false false true '' .git "$ROOT/.git"
@@ -88,6 +107,45 @@
 
 test_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
 
+test_expect_success 'rev-parse --path-format=absolute' '
+	test_one "." "$ROOT/.git" --path-format=absolute --git-dir &&
+	test_one "." "$ROOT/.git" --path-format=absolute --git-common-dir &&
+	test_one "sub/dir" "$ROOT/.git" --path-format=absolute --git-dir &&
+	test_one "sub/dir" "$ROOT/.git" --path-format=absolute --git-common-dir &&
+	test_one "worktree" "$ROOT/.git/worktrees/worktree" --path-format=absolute --git-dir &&
+	test_one "worktree" "$ROOT/.git" --path-format=absolute --git-common-dir &&
+	test_one "." "$ROOT" --path-format=absolute --show-toplevel &&
+	test_one "." "$ROOT/.git/objects" --path-format=absolute --git-path objects &&
+	test_one "." "$ROOT/.git/objects/foo/bar/baz" --path-format=absolute --git-path objects/foo/bar/baz
+'
+
+test_expect_success 'rev-parse --path-format=relative' '
+	test_one "." ".git" --path-format=relative --git-dir &&
+	test_one "." ".git" --path-format=relative --git-common-dir &&
+	test_one "sub/dir" "../../.git" --path-format=relative --git-dir &&
+	test_one "sub/dir" "../../.git" --path-format=relative --git-common-dir &&
+	test_one "worktree" "../.git/worktrees/worktree" --path-format=relative --git-dir &&
+	test_one "worktree" "../.git" --path-format=relative --git-common-dir &&
+	test_one "." "./" --path-format=relative --show-toplevel &&
+	test_one "." ".git/objects" --path-format=relative --git-path objects &&
+	test_one "." ".git/objects/foo/bar/baz" --path-format=relative --git-path objects/foo/bar/baz
+'
+
+test_expect_success '--path-format=relative does not affect --absolute-git-dir' '
+	git rev-parse --path-format=relative --absolute-git-dir >actual &&
+	echo "$ROOT/.git" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--path-format can change in the middle of the command line' '
+	git rev-parse --path-format=absolute --git-dir --path-format=relative --git-path objects/foo/bar >actual &&
+	cat >expect <<-EOF &&
+	$ROOT/.git
+	.git/objects/foo/bar
+	EOF
+	test_cmp expect actual
+'
+
 test_expect_success 'git-common-dir from worktree root' '
 	echo .git >expect &&
 	git rev-parse --git-common-dir >actual &&
@@ -174,8 +232,8 @@
 	test_commit -C super/dir/sub branch1_commit &&
 	git -C super add dir/sub &&
 	test_commit -C super branch1_commit &&
-	git -C super checkout -b branch2 master &&
-	git -C super/dir/sub checkout -b branch2 master &&
+	git -C super checkout -b branch2 main &&
+	git -C super/dir/sub checkout -b branch2 main &&
 	test_commit -C super/dir/sub branch2_commit &&
 	git -C super add dir/sub &&
 	test_commit -C super branch2_commit &&
@@ -185,4 +243,19 @@
 	test_cmp expect out
 '
 
+# at least one external project depends on this behavior:
+test_expect_success 'rev-parse --since= unsqueezed ordering' '
+	x1=--since=1970-01-01T00:00:01Z &&
+	x2=--since=1970-01-01T00:00:02Z &&
+	x3=--since=1970-01-01T00:00:03Z &&
+	git rev-parse $x1 $x1 $x3 $x2 >actual &&
+	cat >expect <<-EOF &&
+	--max-age=1
+	--max-age=1
+	--max-age=3
+	--max-age=2
+	EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index a859abe..b29563f 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -95,7 +95,7 @@
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help output no switches' '
@@ -108,7 +108,7 @@
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help output hidden switches' '
@@ -121,7 +121,7 @@
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help-all output hidden switches' '
@@ -136,7 +136,7 @@
 |EOF
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'test --parseopt invalid switch help output' '
@@ -176,7 +176,7 @@
 |
 END_EXPECT
 	test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'setup expect.1' "
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
index dc9fe3c..bf08102 100755
--- a/t/t1503-rev-parse-verify.sh
+++ b/t/t1503-rev-parse-verify.sh
@@ -6,6 +6,9 @@
 
 exec </dev/null
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 add_line_into_file()
@@ -51,8 +54,8 @@
 	test "$rev_hash3" = "$HASH3" &&
 	rev_hash4=$(git rev-parse --verify $HASH4) &&
 	test "$rev_hash4" = "$HASH4" &&
-	rev_master=$(git rev-parse --verify master) &&
-	test "$rev_master" = "$HASH4" &&
+	rev_main=$(git rev-parse --verify main) &&
+	test "$rev_main" = "$HASH4" &&
 	rev_head=$(git rev-parse --verify HEAD) &&
 	test "$rev_head" = "$HASH4"
 '
@@ -86,8 +89,8 @@
 test_expect_success 'fails silently when using -q with deleted reflogs' '
 	ref=$(git rev-parse HEAD) &&
 	git update-ref --create-reflog -m "message for refs/test" refs/test "$ref" &&
-	git reflog delete --updateref --rewrite refs/test@{0} &&
-	test_must_fail git rev-parse -q --verify refs/test@{0} >error 2>&1 &&
+	git reflog delete --updateref --rewrite refs/test@{1} &&
+	test_must_fail git rev-parse -q --verify refs/test@{1} >error 2>&1 &&
 	test_must_be_empty error
 '
 
@@ -116,27 +119,27 @@
 '
 
 test_expect_success 'use --default' '
-	git rev-parse --verify --default master &&
-	git rev-parse --verify --default master HEAD &&
-	git rev-parse --default master --verify &&
-	git rev-parse --default master --verify HEAD &&
-	git rev-parse --verify HEAD --default master &&
-	test_must_fail git rev-parse --verify foo --default master &&
+	git rev-parse --verify --default main &&
+	git rev-parse --verify --default main HEAD &&
+	git rev-parse --default main --verify &&
+	git rev-parse --default main --verify HEAD &&
+	git rev-parse --verify HEAD --default main &&
+	test_must_fail git rev-parse --verify foo --default main &&
 	test_must_fail git rev-parse --default HEAD --verify bar &&
 	test_must_fail git rev-parse --verify --default HEAD baz &&
 	test_must_fail git rev-parse --default foo --verify &&
 	test_must_fail git rev-parse --verify --default bar
 '
 
-test_expect_success 'master@{n} for various n' '
+test_expect_success 'main@{n} for various n' '
 	N=$(git reflog | wc -l) &&
 	Nm1=$(($N-1)) &&
 	Np1=$(($N+1)) &&
-	git rev-parse --verify master@{0} &&
-	git rev-parse --verify master@{1} &&
-	git rev-parse --verify master@{$Nm1} &&
-	test_must_fail git rev-parse --verify master@{$N} &&
-	test_must_fail git rev-parse --verify master@{$Np1}
+	git rev-parse --verify main@{0} &&
+	git rev-parse --verify main@{1} &&
+	git rev-parse --verify main@{$Nm1} &&
+	test_must_fail git rev-parse --verify main@{$N} &&
+	test_must_fail git rev-parse --verify main@{$Np1}
 '
 
 test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
index 4969edb..2803ca9 100755
--- a/t/t1505-rev-parse-last.sh
+++ b/t/t1505-rev-parse-last.sh
@@ -2,6 +2,9 @@
 
 test_description='test @{-N} syntax'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
@@ -20,12 +23,12 @@
 	make_commit 3 &&
 	git checkout side &&
 	make_commit 4 &&
-	git merge master &&
-	git checkout master
+	git merge main &&
+	git checkout main
 
 '
 
-# 1 -- 2 -- 3 master
+# 1 -- 2 -- 3 main
 #  \         \
 #   \         \
 #    --- 4 --- 5 side
@@ -49,7 +52,7 @@
 '
 
 test_expect_success '@{-2} works' '
-	test_cmp_rev master @{-2}
+	test_cmp_rev main @{-2}
 '
 
 test_expect_success '@{-3} fails' '
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
index e2ae15a..65a154a 100755
--- a/t/t1506-rev-parse-diagnosis.sh
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -4,6 +4,9 @@
 
 exec </dev/null
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_did_you_mean ()
@@ -12,7 +15,7 @@
 	fatal: path '$2$3' $4, but not ${5:-$SQ$3$SQ}
 	hint: Did you mean '$1:$2$3'${2:+ aka $SQ$1:./$3$SQ}?
 	EOF
-	test_i18ncmp expected error
+	test_cmp expected error
 }
 
 HASH_file=
@@ -137,10 +140,10 @@
 '
 
 test_expect_success 'invalid @{n} reference' '
-	test_must_fail git rev-parse master@{99999} >output 2>error &&
+	test_must_fail git rev-parse main@{99999} >output 2>error &&
 	test_must_be_empty output &&
 	test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error  &&
-	test_must_fail git rev-parse --verify master@{99999} >output 2>error &&
+	test_must_fail git rev-parse --verify main@{99999} >output 2>error &&
 	test_must_be_empty output &&
 	test_i18ngrep "log for [^ ]* only has [0-9][0-9]* entries" error
 '
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index dfc0d96..c34714f 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -2,6 +2,9 @@
 
 test_description='test <branch>@{upstream} syntax'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
@@ -10,20 +13,20 @@
 	test_commit 1 &&
 	git checkout -b side &&
 	test_commit 2 &&
-	git checkout master &&
+	git checkout main &&
 	git clone . clone &&
 	test_commit 3 &&
 	(cd clone &&
 	 test_commit 4 &&
 	 git branch --track my-side origin/side &&
-	 git branch --track local-master master &&
+	 git branch --track local-main main &&
 	 git branch --track fun@ny origin/side &&
 	 git branch --track @funny origin/side &&
 	 git branch --track funny@ origin/side &&
-	 git remote add -t master master-only .. &&
-	 git fetch master-only &&
+	 git remote add -t main main-only .. &&
+	 git fetch main-only &&
 	 git branch bad-upstream &&
-	 git config branch.bad-upstream.remote master-only &&
+	 git config branch.bad-upstream.remote main-only &&
 	 git config branch.bad-upstream.merge refs/heads/side
 	)
 '
@@ -39,7 +42,7 @@
 }
 
 test_expect_success '@{upstream} resolves to correct full name' '
-	echo refs/remotes/origin/master >expect &&
+	echo refs/remotes/origin/main >expect &&
 	git -C clone rev-parse --symbolic-full-name @{upstream} >actual &&
 	test_cmp expect actual &&
 	git -C clone rev-parse --symbolic-full-name @{UPSTREAM} >actual &&
@@ -49,7 +52,7 @@
 '
 
 test_expect_success '@{u} resolves to correct full name' '
-	echo refs/remotes/origin/master >expect &&
+	echo refs/remotes/origin/main >expect &&
 	git -C clone rev-parse --symbolic-full-name @{u} >actual &&
 	test_cmp expect actual &&
 	git -C clone rev-parse --symbolic-full-name @{U} >actual &&
@@ -132,7 +135,7 @@
 test_expect_success 'merge my-side@{u} records the correct name' '
 (
 	cd clone &&
-	git checkout master &&
+	git checkout main &&
 	test_might_fail git branch -D new &&
 	git branch -t new my-side@{u} &&
 	git merge -s ours new@{u} &&
@@ -143,24 +146,24 @@
 '
 
 test_expect_success 'branch -d other@{u}' '
-	git checkout -t -b other master &&
+	git checkout -t -b other main &&
 	git branch -d @{u} &&
-	git for-each-ref refs/heads/master >actual &&
+	git for-each-ref refs/heads/main >actual &&
 	test_must_be_empty actual
 '
 
 test_expect_success 'checkout other@{u}' '
-	git branch -f master HEAD &&
-	git checkout -t -b another master &&
+	git branch -f main HEAD &&
+	git checkout -t -b another main &&
 	git checkout @{u} &&
 	git symbolic-ref HEAD >actual &&
-	echo refs/heads/master >expect &&
+	echo refs/heads/main >expect &&
 	test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} works when tracking a local branch' '
-	echo refs/heads/master >expect &&
-	git -C clone rev-parse --symbolic-full-name local-master@{u} >actual &&
+	echo refs/heads/main >expect &&
+	git -C clone rev-parse --symbolic-full-name local-main@{u} >actual &&
 	test_cmp expect actual
 '
 
@@ -169,15 +172,15 @@
 	fatal: no upstream configured for branch ${SQ}non-tracking${SQ}
 	EOF
 	error_message non-tracking@{u} &&
-	test_i18ncmp expect error
+	test_cmp expect error
 '
 
 test_expect_success '@{u} error message when no upstream' '
 	cat >expect <<-EOF &&
-	fatal: no upstream configured for branch ${SQ}master${SQ}
+	fatal: no upstream configured for branch ${SQ}main${SQ}
 	EOF
 	test_must_fail git rev-parse --verify @{u} 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} error message with misspelt branch' '
@@ -185,7 +188,7 @@
 	fatal: no such branch: ${SQ}no-such-branch${SQ}
 	EOF
 	error_message no-such-branch@{u} &&
-	test_i18ncmp expect error
+	test_cmp expect error
 '
 
 test_expect_success '@{u} error message when not on a branch' '
@@ -194,7 +197,7 @@
 	EOF
 	git checkout HEAD^0 &&
 	test_must_fail git rev-parse --verify @{u} 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} error message if upstream branch not fetched' '
@@ -202,20 +205,20 @@
 	fatal: upstream branch ${SQ}refs/heads/side${SQ} not stored as a remote-tracking branch
 	EOF
 	error_message bad-upstream@{u} &&
-	test_i18ncmp expect error
+	test_cmp expect error
 '
 
 test_expect_success 'pull works when tracking a local branch' '
 (
 	cd clone &&
-	git checkout local-master &&
+	git checkout local-main &&
 	git pull
 )
 '
 
 # makes sense if the previous one succeeded
 test_expect_success '@{u} works when tracking a local branch' '
-	echo refs/heads/master >expect &&
+	echo refs/heads/main >expect &&
 	git -C clone rev-parse --symbolic-full-name @{u} >actual &&
 	test_cmp expect actual
 '
@@ -224,7 +227,7 @@
 	commit=$(git rev-parse HEAD) &&
 	cat >expect <<-EOF &&
 	commit $commit
-	Reflog: master@{0} (C O Mitter <committer@example.com>)
+	Reflog: main@{0} (C O Mitter <committer@example.com>)
 	Reflog message: branch: Created from HEAD
 	Author: A U Thor <author@example.com>
 	Date:   Thu Apr 7 15:15:13 2005 -0700
@@ -239,7 +242,7 @@
 	commit=$(git rev-parse HEAD) &&
 	cat >expect <<-EOF &&
 	commit $commit
-	Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
+	Reflog: main@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
 	Reflog message: branch: Created from HEAD
 	Author: A U Thor <author@example.com>
 	Date:   Thu Apr 7 15:15:13 2005 -0700
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
index 4a9964e..87a4286 100755
--- a/t/t1508-at-combinations.sh
+++ b/t/t1508-at-combinations.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test various @{X} syntax combinations together'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check() {
@@ -30,8 +33,8 @@
 }
 
 test_expect_success 'setup' '
-	test_commit master-one &&
-	test_commit master-two &&
+	test_commit main-one &&
+	test_commit main-two &&
 	git checkout -b upstream-branch &&
 	test_commit upstream-one &&
 	test_commit upstream-two &&
@@ -47,7 +50,7 @@
 	git checkout -b new-branch &&
 	test_commit new-one &&
 	test_commit new-two &&
-	git branch -u master old-branch &&
+	git branch -u main old-branch &&
 	git branch -u upstream-branch new-branch
 '
 
@@ -62,8 +65,8 @@
 check "@{u}" ref refs/heads/upstream-branch
 check "HEAD@{u}" ref refs/heads/upstream-branch
 check "@{u}@{1}" commit upstream-one
-check "@{-1}@{u}" ref refs/heads/master
-check "@{-1}@{u}@{1}" commit master-one
+check "@{-1}@{u}" ref refs/heads/main
+check "@{-1}@{u}@{1}" commit main-one
 check "@" commit new-two
 check "@@{u}" ref refs/heads/upstream-branch
 check "@@/at-test" ref refs/heads/@@/at-test
@@ -99,4 +102,17 @@
 check "@:normal" blob content
 check "@:fun@ny" blob content
 
+test_expect_success '@{1} works with only one reflog entry' '
+	git checkout -B newbranch main &&
+	git reflog expire --expire=now refs/heads/newbranch &&
+	git commit --allow-empty -m "first after expiration" &&
+	test_cmp_rev newbranch~ newbranch@{1}
+'
+
+test_expect_success '@{0} works with empty reflog' '
+	git checkout -B newbranch main &&
+	git reflog expire --expire=now refs/heads/newbranch &&
+	test_cmp_rev newbranch newbranch@{0}
+'
+
 test_done
diff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh
index fd2f7ab..553a3f6 100755
--- a/t/t1509-root-work-tree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -221,7 +221,7 @@
 	rm -rf /.git &&
 	echo "Initialized empty Git repository in /.git/" > expected &&
 	git init > result &&
-	test_i18ncmp expected result
+	test_cmp expected result
 '
 
 test_vars 'auto gitdir, root' ".git" "/" ""
@@ -246,7 +246,7 @@
 	cd / &&
 	echo "Initialized empty Git repository in /" > expected &&
 	git init --bare > result &&
-	test_i18ncmp expected result
+	test_cmp expected result
 '
 
 test_vars 'auto gitdir, root' "." "" ""
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index e0a49a6..6ecfed8 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -2,6 +2,9 @@
 
 test_description='tests for ref^{stuff}'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -15,7 +18,7 @@
 	git commit -m Initial &&
 	git tag -a -m commit commit-tag &&
 	git branch ref &&
-	git checkout master &&
+	git checkout main &&
 	echo modified >>a-blob &&
 	git add -u &&
 	git commit -m Modified &&
@@ -73,52 +76,52 @@
 '
 
 test_expect_success 'ref^{/.}' '
-	git rev-parse master >expected &&
-	git rev-parse master^{/.} >actual &&
+	git rev-parse main >expected &&
+	git rev-parse main^{/.} >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'ref^{/non-existent}' '
-	test_must_fail git rev-parse master^{/non-existent}
+	test_must_fail git rev-parse main^{/non-existent}
 '
 
 test_expect_success 'ref^{/Initial}' '
 	git rev-parse ref >expected &&
-	git rev-parse master^{/Initial} >actual &&
+	git rev-parse main^{/Initial} >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'ref^{/!Exp}' '
-	test_must_fail git rev-parse master^{/!Exp}
+	test_must_fail git rev-parse main^{/!Exp}
 '
 
 test_expect_success 'ref^{/!}' '
-	test_must_fail git rev-parse master^{/!}
+	test_must_fail git rev-parse main^{/!}
 '
 
 test_expect_success 'ref^{/!!Exp}' '
 	git rev-parse expref >expected &&
-	git rev-parse master^{/!!Exp} >actual &&
+	git rev-parse main^{/!!Exp} >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'ref^{/!-}' '
-	test_must_fail git rev-parse master^{/!-}
+	test_must_fail git rev-parse main^{/!-}
 '
 
 test_expect_success 'ref^{/!-.}' '
-	test_must_fail git rev-parse master^{/!-.}
+	test_must_fail git rev-parse main^{/!-.}
 '
 
 test_expect_success 'ref^{/!-non-existent}' '
-	git rev-parse master >expected &&
-	git rev-parse master^{/!-non-existent} >actual &&
+	git rev-parse main >expected &&
+	git rev-parse main^{/!-non-existent} >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'ref^{/!-Changed}' '
 	git rev-parse expref >expected &&
-	git rev-parse master^{/!-Changed} >actual &&
+	git rev-parse main^{/!-Changed} >actual &&
 	test_cmp expected actual
 '
 
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index 18fa6cf..7891a6b 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -20,6 +20,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq SHA1
@@ -48,7 +51,7 @@
 
 test_expect_success 'warn ambiguity when no candidate matches type hint' '
 	test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
-	test_i18ngrep "short SHA1 000000000 is ambiguous" actual
+	test_i18ngrep "short object ID 000000000 is ambiguous" actual
 '
 
 test_expect_success 'disambiguate tree-ish' '
@@ -212,7 +215,7 @@
 	side=$(git rev-parse HEAD) &&
 
 	# commit 000000000066
-	git checkout master &&
+	git checkout main &&
 
 	# If you use recursive, merge will fail and you will need to
 	# clean up a0blgqsjc as well.  If you use resolve, merge will
@@ -311,39 +314,39 @@
 	grep "refname.*${REF}.*ambiguous" err
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' '
+test_expect_success 'ambiguity errors are not repeated (raw)' '
 	test_must_fail git rev-parse 00000 2>stderr &&
 	grep "is ambiguous" stderr >errors &&
 	test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' '
+test_expect_success 'ambiguity errors are not repeated (treeish)' '
 	test_must_fail git rev-parse 00000:foo 2>stderr &&
 	grep "is ambiguous" stderr >errors &&
 	test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' '
+test_expect_success 'ambiguity errors are not repeated (peel)' '
 	test_must_fail git rev-parse 00000^{commit} 2>stderr &&
 	grep "is ambiguous" stderr >errors &&
 	test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '
+test_expect_success 'ambiguity hints' '
 	test_must_fail git rev-parse 000000000 2>stderr &&
 	grep ^hint: stderr >hints &&
 	# 16 candidates, plus one intro line
 	test_line_count = 17 hints
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '
+test_expect_success 'ambiguity hints respect type' '
 	test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
 	grep ^hint: stderr >hints &&
 	# 5 commits, 1 tag (which is a committish), plus intro line
 	test_line_count = 7 hints
 '
 
-test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' '
+test_expect_success 'failed type-selector still shows hint' '
 	# these two blobs share the same prefix "ee3d", but neither
 	# will pass for a commit
 	echo 851 | git hash-object --stdin -w &&
@@ -367,7 +370,7 @@
 		git -c core.disambiguate=committish rev-parse $sha1^{tree}
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' '
+test_expect_success 'ambiguous commits are printed by type first, then hash order' '
 	test_must_fail git rev-parse 0000 2>stderr &&
 	grep ^hint: stderr >hints &&
 	grep 0000 hints >objects &&
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
index 87ec3ae..5f437be 100755
--- a/t/t1513-rev-parse-prefix.sh
+++ b/t/t1513-rev-parse-prefix.sh
@@ -2,6 +2,9 @@
 
 test_description='Tests for rev-parse --prefix'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -76,9 +79,9 @@
 '
 
 test_expect_success 'file and refs with prefix' '
-	git rev-parse --prefix sub1/ master file1 >actual &&
+	git rev-parse --prefix sub1/ main file1 >actual &&
 	cat <<-EOF >expected &&
-	$(git rev-parse master)
+	$(git rev-parse main)
 	sub1/file1
 	EOF
 	test_cmp expected actual
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
index 788cc91..d868a08 100755
--- a/t/t1514-rev-parse-push.sh
+++ b/t/t1514-rev-parse-push.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test <branch>@{push} syntax'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 resolve () {
@@ -16,24 +19,24 @@
 	git remote add other other.git &&
 	test_commit base &&
 	git push origin HEAD &&
-	git branch --set-upstream-to=origin/master master &&
-	git branch --track topic origin/master &&
+	git branch --set-upstream-to=origin/main main &&
+	git branch --track topic origin/main &&
 	git push origin topic &&
 	git push other topic
 '
 
 test_expect_success '@{push} with default=nothing' '
 	test_config push.default nothing &&
-	test_must_fail git rev-parse master@{push} &&
-	test_must_fail git rev-parse master@{PUSH} &&
-	test_must_fail git rev-parse master@{PuSH}
+	test_must_fail git rev-parse main@{push} &&
+	test_must_fail git rev-parse main@{PUSH} &&
+	test_must_fail git rev-parse main@{PuSH}
 '
 
 test_expect_success '@{push} with default=simple' '
 	test_config push.default simple &&
-	resolve master@{push} refs/remotes/origin/master &&
-	resolve master@{PUSH} refs/remotes/origin/master &&
-	resolve master@{pUSh} refs/remotes/origin/master
+	resolve main@{push} refs/remotes/origin/main &&
+	resolve main@{PUSH} refs/remotes/origin/main &&
+	resolve main@{pUSh} refs/remotes/origin/main
 '
 
 test_expect_success 'triangular @{push} fails with default=simple' '
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
index b7c31aa..c9b9e71 100755
--- a/t/t1600-index.sh
+++ b/t/t1600-index.sh
@@ -18,7 +18,7 @@
 			warning: GIT_INDEX_VERSION set, but the value is invalid.
 			Using version Z
 		EOF
-		test_i18ncmp expect.err actual.err
+		test_cmp expect.err actual.err
 	)
 '
 
@@ -32,7 +32,7 @@
 			warning: GIT_INDEX_VERSION set, but the value is invalid.
 			Using version Z
 		EOF
-		test_i18ncmp expect.err actual.err
+		test_cmp expect.err actual.err
 	)
 '
 
@@ -55,7 +55,7 @@
 			warning: index.version set, but the value is invalid.
 			Using version Z
 		EOF
-		test_i18ncmp expect.err actual.err
+		test_cmp expect.err actual.err
 	)
 '
 
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index 12a5568..986baa6 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -2,6 +2,9 @@
 
 test_description='split index mode tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # We need total control of index splitting here
@@ -496,7 +499,7 @@
 		test_commit initial &&
 		git checkout -b side-branch &&
 		test_commit extra &&
-		git checkout master &&
+		git checkout main &&
 		git update-index --split-index &&
 		test_commit more &&
 		# must not write a new shareindex, or we wont catch the problem
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
index 7b25a35..7705e3a 100755
--- a/t/t2006-checkout-index-basic.sh
+++ b/t/t2006-checkout-index-basic.sh
@@ -77,4 +77,27 @@
 	'
 done
 
+test_expect_success 'checkout-index --temp correctly reports error on missing blobs' '
+	test_when_finished git reset --hard &&
+	missing_blob=$(echo "no such blob here" | git hash-object --stdin) &&
+	cat >objs <<-EOF &&
+	100644 $missing_blob	file
+	120000 $missing_blob	symlink
+	EOF
+	git update-index --index-info <objs &&
+
+	test_must_fail git checkout-index --temp symlink file 2>stderr &&
+	test_i18ngrep "unable to read sha1 file of file ($missing_blob)" stderr &&
+	test_i18ngrep "unable to read sha1 file of symlink ($missing_blob)" stderr
+'
+
+test_expect_success 'checkout-index --temp correctly reports error for submodules' '
+	git init sub &&
+	test_commit -C sub file &&
+	git submodule add ./sub &&
+	git commit -m sub &&
+	test_must_fail git checkout-index --temp sub 2>stderr &&
+	test_i18ngrep "cannot create temporary submodule sub" stderr
+'
+
 test_done
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
index fc9aad5..6f0b90c 100755
--- a/t/t2007-checkout-symlink.sh
+++ b/t/t2007-checkout-symlink.sh
@@ -4,6 +4,9 @@
 
 test_description='git checkout to switch between branches with symlink<->dir'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -12,14 +15,14 @@
 	echo hello >frotz/filfre &&
 	git add frotz/filfre &&
 	test_tick &&
-	git commit -m "master has file frotz/filfre" &&
+	git commit -m "main has file frotz/filfre" &&
 
 	git branch side &&
 
 	echo goodbye >nitfol &&
 	git add nitfol &&
 	test_tick &&
-	git commit -m "master adds file nitfol" &&
+	git commit -m "main adds file nitfol" &&
 
 	git checkout side &&
 
@@ -34,13 +37,13 @@
 
 test_expect_success 'switch from symlink to dir' '
 
-	git checkout master
+	git checkout main
 
 '
 
-test_expect_success 'Remove temporary directories & switch to master' '
+test_expect_success 'Remove temporary directories & switch to main' '
 	rm -fr frotz xyzzy nitfol &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'switch from dir to symlink' '
diff --git a/t/t2009-checkout-statinfo.sh b/t/t2009-checkout-statinfo.sh
index f3c2152..b054063 100755
--- a/t/t2009-checkout-statinfo.sh
+++ b/t/t2009-checkout-statinfo.sh
@@ -2,6 +2,9 @@
 
 test_description='checkout should leave clean stat info'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -21,13 +24,13 @@
 	git reset --hard &&
 	test "$(git diff-files --raw)" = "" &&
 
-	git checkout master &&
+	git checkout main &&
 	test "$(git diff-files --raw)" = "" &&
 
 	git checkout side &&
 	test "$(git diff-files --raw)" = "" &&
 
-	git checkout master &&
+	git checkout main &&
 	test "$(git diff-files --raw)" = ""
 
 '
@@ -37,13 +40,13 @@
 	git reset --hard &&
 	test "$(git diff-files --raw)" = "" &&
 
-	git checkout master world &&
+	git checkout main world &&
 	test "$(git diff-files --raw)" = "" &&
 
 	git checkout side world &&
 	test "$(git diff-files --raw)" = "" &&
 
-	git checkout master world &&
+	git checkout main world &&
 	test "$(git diff-files --raw)" = ""
 
 '
diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh
index 2e47fe0..6e87573 100755
--- a/t/t2010-checkout-ambiguous.sh
+++ b/t/t2010-checkout-ambiguous.sh
@@ -2,6 +2,9 @@
 
 test_description='checkout and pathspecs/refspecs ambiguities'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -17,7 +20,7 @@
 '
 
 test_expect_success 'branch switching' '
-	test "refs/heads/master" = "$(git symbolic-ref HEAD)" &&
+	test "refs/heads/main" = "$(git symbolic-ref HEAD)" &&
 	git checkout world -- &&
 	test "refs/heads/world" = "$(git symbolic-ref HEAD)"
 '
@@ -57,7 +60,7 @@
 '
 
 test_expect_success 'accurate error message with more than one ref' '
-	test_must_fail git checkout HEAD master -- 2>actual &&
+	test_must_fail git checkout HEAD main -- 2>actual &&
 	test_i18ngrep 2 actual &&
 	test_i18ngrep "one reference expected, 2 given" actual
 '
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
index 0e8d56a..e52022e 100755
--- a/t/t2011-checkout-invalid-head.sh
+++ b/t/t2011-checkout-invalid-head.sh
@@ -2,6 +2,9 @@
 
 test_description='checkout switching away from an invalid branch'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -11,12 +14,12 @@
 '
 
 test_expect_success 'checkout should not start branch from a tree' '
-	test_must_fail git checkout -b newbranch master^{tree}
+	test_must_fail git checkout -b newbranch main^{tree}
 '
 
-test_expect_success 'checkout master from invalid HEAD' '
+test_expect_success 'checkout main from invalid HEAD' '
 	echo $ZERO_OID >.git/HEAD &&
-	git checkout master --
+	git checkout main --
 '
 
 test_expect_success 'checkout notices failure to lock HEAD' '
@@ -26,7 +29,7 @@
 '
 
 test_expect_success 'create ref directory/file conflict scenario' '
-	git update-ref refs/heads/outer/inner master &&
+	git update-ref refs/heads/outer/inner main &&
 
 	# do not rely on symbolic-ref to get a known state,
 	# as it may use the same code we are testing
@@ -37,12 +40,12 @@
 
 test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
 	reset_to_df &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
 	reset_to_df &&
-	git checkout --detach master
+	git checkout --detach main
 '
 
 test_expect_success 'pack refs' '
@@ -51,11 +54,11 @@
 
 test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
 	reset_to_df &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
 	reset_to_df &&
-	git checkout --detach master
+	git checkout --detach main
 '
 test_done
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index e7ba8c5..0e7d47a 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -2,16 +2,15 @@
 
 test_description='checkout can switch to last branch and merge base'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-	echo hello >world &&
-	git add world &&
-	git commit -m initial &&
+	test_commit initial world hello &&
 	git branch other &&
-	echo "hello again" >>world &&
-	git add world &&
-	git commit -m second
+	test_commit --append second world "hello again"
 '
 
 test_expect_success '"checkout -" does not work initially' '
@@ -24,7 +23,7 @@
 
 test_expect_success '"checkout -" switches back' '
 	git checkout - &&
-	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
 '
 
 test_expect_success '"checkout -" switches forth' '
@@ -93,61 +92,59 @@
 
 test_expect_success 'merge base test setup' '
 	git checkout -b another other &&
-	echo "hello again" >>world &&
-	git add world &&
-	git commit -m third
+	test_commit --append third world "hello again"
 '
 
-test_expect_success 'another...master' '
+test_expect_success 'another...main' '
 	git checkout another &&
-	git checkout another...master &&
-	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+	git checkout another...main &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
 '
 
-test_expect_success '...master' '
+test_expect_success '...main' '
 	git checkout another &&
-	git checkout ...master &&
-	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+	git checkout ...main &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
 '
 
-test_expect_success 'master...' '
+test_expect_success 'main...' '
 	git checkout another &&
-	git checkout master... &&
-	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+	git checkout main... &&
+	test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
 '
 
 test_expect_success '"checkout -" works after a rebase A' '
-	git checkout master &&
+	git checkout main &&
 	git checkout other &&
-	git rebase master &&
+	git rebase main &&
 	git checkout - &&
-	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
 '
 
 test_expect_success '"checkout -" works after a rebase A B' '
-	git branch moodle master~1 &&
-	git checkout master &&
+	git branch moodle main~1 &&
+	git checkout main &&
 	git checkout other &&
-	git rebase master moodle &&
+	git rebase main moodle &&
 	git checkout - &&
-	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
 '
 
 test_expect_success '"checkout -" works after a rebase -i A' '
-	git checkout master &&
+	git checkout main &&
 	git checkout other &&
-	git rebase -i master &&
+	git rebase -i main &&
 	git checkout - &&
-	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
 '
 
 test_expect_success '"checkout -" works after a rebase -i A B' '
-	git branch foodle master~1 &&
-	git checkout master &&
+	git branch foodle main~1 &&
+	git checkout main &&
 	git checkout other &&
-	git rebase master foodle &&
+	git rebase main foodle &&
 	git checkout - &&
-	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+	test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
 '
 
 test_done
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index 37bdced..a972121 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='checkout from unborn branch'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -11,7 +14,7 @@
 	 git add file &&
 	 git commit -m base
 	) &&
-	git fetch parent master:origin
+	git fetch parent main:origin
 '
 
 test_expect_success 'checkout from unborn preserves untracked files' '
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
index 655f278..c7adbdd 100755
--- a/t/t2017-checkout-orphan.sh
+++ b/t/t2017-checkout-orphan.sh
@@ -7,6 +7,9 @@
 
 Main Tests for --orphan functionality.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 TEST_FILE=foo
@@ -29,34 +32,34 @@
 	test_tick &&
 	git commit -m "Third Commit" &&
 	test_must_fail git rev-parse --verify HEAD^ &&
-	git diff-tree --quiet master alpha
+	git diff-tree --quiet main alpha
 '
 
 test_expect_success '--orphan creates a new orphan branch from <start_point>' '
-	git checkout master &&
-	git checkout --orphan beta master^ &&
+	git checkout main &&
+	git checkout --orphan beta main^ &&
 	test_must_fail git rev-parse --verify HEAD &&
 	test "refs/heads/beta" = "$(git symbolic-ref HEAD)" &&
 	test_tick &&
 	git commit -m "Fourth Commit" &&
 	test_must_fail git rev-parse --verify HEAD^ &&
-	git diff-tree --quiet master^ beta
+	git diff-tree --quiet main^ beta
 '
 
 test_expect_success '--orphan must be rejected with -b' '
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git checkout --orphan new -b newer &&
-	test refs/heads/master = "$(git symbolic-ref HEAD)"
+	test refs/heads/main = "$(git symbolic-ref HEAD)"
 '
 
 test_expect_success '--orphan must be rejected with -t' '
-	git checkout master &&
-	test_must_fail git checkout --orphan new -t master &&
-	test refs/heads/master = "$(git symbolic-ref HEAD)"
+	git checkout main &&
+	test_must_fail git checkout --orphan new -t main &&
+	test refs/heads/main = "$(git symbolic-ref HEAD)"
 '
 
 test_expect_success '--orphan ignores branch.autosetupmerge' '
-	git checkout master &&
+	git checkout main &&
 	git config branch.autosetupmerge always &&
 	git checkout --orphan gamma &&
 	test -z "$(git config branch.gamma.merge)" &&
@@ -65,7 +68,7 @@
 '
 
 test_expect_success '--orphan makes reflog by default' '
-	git checkout master &&
+	git checkout main &&
 	git config --unset core.logAllRefUpdates &&
 	git checkout --orphan delta &&
 	test_must_fail git rev-parse --verify delta@{0} &&
@@ -74,7 +77,7 @@
 '
 
 test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
-	git checkout master &&
+	git checkout main &&
 	git config core.logAllRefUpdates false &&
 	git checkout --orphan epsilon &&
 	test_must_fail git rev-parse --verify epsilon@{0} &&
@@ -83,7 +86,7 @@
 '
 
 test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
-	git checkout master &&
+	git checkout main &&
 	git checkout -l --orphan zeta &&
 	test_must_fail git rev-parse --verify zeta@{0} &&
 	git commit -m Zeta &&
@@ -91,33 +94,33 @@
 '
 
 test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
-	git checkout master &&
+	git checkout main &&
 	git checkout -l --orphan eta &&
 	test_must_fail git rev-parse --verify eta@{0} &&
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git rev-parse --verify eta@{0}
 '
 
 test_expect_success '--orphan is rejected with an existing name' '
-	git checkout master &&
-	test_must_fail git checkout --orphan master &&
-	test refs/heads/master = "$(git symbolic-ref HEAD)"
+	git checkout main &&
+	test_must_fail git checkout --orphan main &&
+	test refs/heads/main = "$(git symbolic-ref HEAD)"
 '
 
 test_expect_success '--orphan refuses to switch if a merge is needed' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard &&
 	echo local >>"$TEST_FILE" &&
 	cat "$TEST_FILE" >"$TEST_FILE.saved" &&
-	test_must_fail git checkout --orphan new master^ &&
-	test refs/heads/master = "$(git symbolic-ref HEAD)" &&
+	test_must_fail git checkout --orphan new main^ &&
+	test refs/heads/main = "$(git symbolic-ref HEAD)" &&
 	test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
 	git diff-index --quiet --cached HEAD &&
 	git reset --hard
 '
 
 test_expect_success 'cannot --detach on an unborn branch' '
-	git checkout master &&
+	git checkout main &&
 	git checkout --orphan new &&
 	test_must_fail git checkout --detach
 '
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
index 5f761bc..93be1c0 100755
--- a/t/t2018-checkout-branch.sh
+++ b/t/t2018-checkout-branch.sh
@@ -150,7 +150,7 @@
 	git checkout branch2 &&
 	echo  >expect "fatal: A branch named '\''branch1'\'' already exists." &&
 	test_must_fail git checkout -b @{-1} 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
index b748db9..bc46713 100755
--- a/t/t2020-checkout-detach.sh
+++ b/t/t2020-checkout-detach.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='checkout into detached HEAD state'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_detached () {
@@ -22,7 +25,7 @@
 }
 
 reset () {
-	git checkout master &&
+	git checkout main &&
 	check_not_detached
 }
 
@@ -85,7 +88,7 @@
 
 test_expect_success 'checkout --detach errors out for extra argument' '
 	reset &&
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git checkout --detach tag one.t &&
 	check_not_detached
 '
@@ -113,7 +116,7 @@
 	echo new content >orphan &&
 	git commit -a -m orphan2 &&
 	orphan2=$(git rev-parse HEAD) &&
-	git checkout master 2>stderr
+	git checkout main 2>stderr
 '
 
 test_expect_success 'checkout warns on orphan commits: output' '
@@ -132,7 +135,7 @@
 test_expect_success 'checkout does not warn leaving ref tip' '
 	reset &&
 	git checkout --detach two &&
-	git checkout master 2>stderr
+	git checkout main 2>stderr
 '
 
 test_expect_success 'checkout does not warn leaving ref tip' '
@@ -142,7 +145,7 @@
 test_expect_success 'checkout does not warn leaving reachable commit' '
 	reset &&
 	git checkout --detach HEAD^ &&
-	git checkout master 2>stderr
+	git checkout main 2>stderr
 '
 
 test_expect_success 'checkout does not warn leaving reachable commit' '
@@ -150,17 +153,17 @@
 '
 
 cat >expect <<'EOF'
-Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
+Your branch is behind 'main' by 1 commit, and can be fast-forwarded.
   (use "git pull" to update your local branch)
 EOF
 test_expect_success 'tracking count is accurate after orphan check' '
 	reset &&
-	git branch child master^ &&
+	git branch child main^ &&
 	git config branch.child.remote . &&
-	git config branch.child.merge refs/heads/master &&
+	git config branch.child.merge refs/heads/main &&
 	git checkout child^ &&
 	git checkout child >stdout &&
-	test_i18ncmp expect stdout
+	test_cmp expect stdout
 '
 
 test_expect_success 'no advice given for explicit detached head state' '
@@ -189,9 +192,9 @@
 # Detached HEAD tests for GIT_PRINT_SHA1_ELLIPSIS (new format)
 test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not asked to' "
 
-	commit=$(git rev-parse --short=12 master^) &&
-	commit2=$(git rev-parse --short=12 master~2) &&
-	commit3=$(git rev-parse --short=12 master~3) &&
+	commit=$(git rev-parse --short=12 main^) &&
+	commit2=$(git rev-parse --short=12 main~2) &&
+	commit3=$(git rev-parse --short=12 main~3) &&
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
@@ -234,15 +237,15 @@
 	sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
 	git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 1st_detach actual &&
+	test_cmp 1st_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS="no" git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 2nd_detach actual &&
+	test_cmp 2nd_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS= git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 3rd_detach actual &&
+	test_cmp 3rd_detach actual &&
 
 	sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
 
@@ -253,17 +256,17 @@
 	# Make no mention of the env var at all
 	git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 1st_detach actual &&
+	test_cmp 1st_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS='nope' &&
 	git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 2nd_detach actual &&
+	test_cmp 2nd_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS=nein &&
 	git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 3rd_detach actual &&
+	test_cmp 3rd_detach actual &&
 
 	true
 "
@@ -271,9 +274,9 @@
 # Detached HEAD tests for GIT_PRINT_SHA1_ELLIPSIS (old format)
 test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked to' "
 
-	commit=$(git rev-parse --short=12 master^) &&
-	commit2=$(git rev-parse --short=12 master~2) &&
-	commit3=$(git rev-parse --short=12 master~3) &&
+	commit=$(git rev-parse --short=12 main^) &&
+	commit2=$(git rev-parse --short=12 main~2) &&
+	commit3=$(git rev-parse --short=12 main~3) &&
 
 	# The first detach operation is more chatty than the following ones.
 	cat >1st_detach <<-EOF &&
@@ -316,15 +319,15 @@
 
 	GIT_PRINT_SHA1_ELLIPSIS=yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 1st_detach actual &&
+	test_cmp 1st_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS=Yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 2nd_detach actual &&
+	test_cmp 2nd_detach actual &&
 
 	GIT_PRINT_SHA1_ELLIPSIS=YES git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
 	check_detached &&
-	test_i18ncmp 3rd_detach actual &&
+	test_cmp 3rd_detach actual &&
 
 	true
 "
diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh
index 6844afa..c49ba7f 100755
--- a/t/t2022-checkout-paths.sh
+++ b/t/t2022-checkout-paths.sh
@@ -1,19 +1,22 @@
 #!/bin/sh
 
 test_description='checkout $tree -- $paths'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
 	mkdir dir &&
-	>dir/master &&
+	>dir/main &&
 	echo common >dir/common &&
-	git add dir/master dir/common &&
-	test_tick && git commit -m "master has dir/master" &&
+	git add dir/main dir/common &&
+	test_tick && git commit -m "main has dir/main" &&
 	git checkout -b next &&
-	git mv dir/master dir/next0 &&
+	git mv dir/main dir/next0 &&
 	echo next >dir/next1 &&
 	git add dir &&
-	test_tick && git commit -m "next has dir/next but not dir/master"
+	test_tick && git commit -m "next has dir/next but not dir/main"
 '
 
 test_expect_success 'checking out paths out of a tree does not clobber unrelated paths' '
@@ -26,11 +29,11 @@
 	echo untracked >expect.next2 &&
 	cat expect.next2 >dir/next2 &&
 
-	git checkout master dir &&
+	git checkout main dir &&
 
 	test_cmp expect.common dir/common &&
-	test_path_is_file dir/master &&
-	git diff --exit-code master dir/master &&
+	test_path_is_file dir/main &&
+	git diff --exit-code main dir/main &&
 
 	test_path_is_missing dir/next0 &&
 	test_cmp expect.next1 dir/next1 &&
@@ -52,11 +55,11 @@
 	EOF
 	git update-index --index-info <expect.next0 &&
 
-	git checkout master dir &&
+	git checkout main dir &&
 
 	test_cmp expect.common dir/common &&
-	test_path_is_file dir/master &&
-	git diff --exit-code master dir/master &&
+	test_path_is_file dir/main &&
+	git diff --exit-code main dir/main &&
 	git ls-files -s dir/next0 >actual.next0 &&
 	test_cmp expect.next0 actual.next0
 '
diff --git a/t/t2023-checkout-m.sh b/t/t2023-checkout-m.sh
index fca3f85..7b327b7 100755
--- a/t/t2023-checkout-m.sh
+++ b/t/t2023-checkout-m.sh
@@ -4,21 +4,24 @@
 
 Ensures that checkout -m on a resolved file restores the conflicted file'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
 	test_tick &&
 	test_commit both.txt both.txt initial &&
 	git branch topic &&
-	test_commit modified_in_master both.txt in_master &&
-	test_commit added_in_master each.txt in_master &&
+	test_commit modified_in_main both.txt in_main &&
+	test_commit added_in_main each.txt in_main &&
 	git checkout topic &&
 	test_commit modified_in_topic both.txt in_topic &&
 	test_commit added_in_topic each.txt in_topic
 '
 
-test_expect_success 'git merge master' '
-    test_must_fail git merge master
+test_expect_success 'git merge main' '
+    test_must_fail git merge main
 '
 
 clean_branchnames () {
@@ -61,7 +64,7 @@
 		git checkout topic &&
 		echo c >a &&
 		C_OBJ=$(git hash-object a) &&
-		git checkout -m master &&
+		git checkout -m main &&
 		test_cmp_rev :1:a $A_OBJ &&
 		test_cmp_rev :2:a $B_OBJ &&
 		test_cmp_rev :3:a $C_OBJ &&
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index a4f8d3a..4a1c901 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -29,11 +29,11 @@
 }
 
 test_expect_success 'setup' '
-	test_commit my_master &&
+	test_commit my_main &&
 	git init repo_a &&
 	(
 		cd repo_a &&
-		test_commit a_master &&
+		test_commit a_main &&
 		git checkout -b foo &&
 		test_commit a_foo &&
 		git checkout -b bar &&
@@ -44,7 +44,7 @@
 	git init repo_b &&
 	(
 		cd repo_b &&
-		test_commit b_master &&
+		test_commit b_main &&
 		git checkout -b foo &&
 		test_commit b_foo &&
 		git checkout -b baz &&
@@ -60,23 +60,23 @@
 '
 
 test_expect_success 'checkout of non-existing branch fails' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D xyzzy &&
 
 	test_must_fail git checkout xyzzy &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/xyzzy &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout of branch from multiple remotes fails #1' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D foo &&
 
 	test_must_fail git checkout foo &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/foo &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'when arg matches multiple remotes, do not fallback to interpreting as pathspec' '
@@ -100,21 +100,21 @@
 '
 
 test_expect_success 'checkout of branch from multiple remotes fails with advice' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D foo &&
 	test_must_fail git checkout foo 2>stderr &&
-	test_branch master &&
+	test_branch main &&
 	status_uno_is_clean &&
 	test_i18ngrep "^hint: " stderr &&
 	test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
 		checkout foo 2>stderr &&
-	test_branch master &&
+	test_branch main &&
 	status_uno_is_clean &&
 	test_i18ngrep ! "^hint: " stderr
 '
 
 test_expect_success PERL 'checkout -p with multiple remotes does not print advice' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D foo &&
 
 	git checkout -p foo 2>stderr &&
@@ -123,7 +123,7 @@
 '
 
 test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D foo &&
 
@@ -135,7 +135,7 @@
 '
 
 test_expect_success 'checkout of branch from a single remote succeeds #1' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D bar &&
 
 	git checkout bar &&
@@ -146,7 +146,7 @@
 '
 
 test_expect_success 'checkout of branch from a single remote succeeds #2' '
-	git checkout -B master &&
+	git checkout -B main &&
 	test_might_fail git branch -D baz &&
 
 	git checkout baz &&
@@ -157,33 +157,33 @@
 '
 
 test_expect_success '--no-guess suppresses branch auto-vivification' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D bar &&
 
 	test_must_fail git checkout --no-guess bar &&
 	test_must_fail git rev-parse --verify refs/heads/bar &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout.guess = false suppresses branch auto-vivification' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D bar &&
 
 	test_config checkout.guess false &&
 	test_must_fail git checkout bar &&
 	test_must_fail git rev-parse --verify refs/heads/bar &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'setup more remotes with unconventional refspecs' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	git init repo_c &&
 	(
 		cd repo_c &&
-		test_commit c_master &&
+		test_commit c_main &&
 		git checkout -b bar &&
 		test_commit c_bar &&
 		git checkout -b spam &&
@@ -192,7 +192,7 @@
 	git init repo_d &&
 	(
 		cd repo_d &&
-		test_commit d_master &&
+		test_commit d_main &&
 		git checkout -b baz &&
 		test_commit d_baz &&
 		git checkout -b eggs &&
@@ -208,29 +208,29 @@
 '
 
 test_expect_success 'checkout of branch from multiple remotes fails #2' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D bar &&
 
 	test_must_fail git checkout bar &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/bar &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout of branch from multiple remotes fails #3' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D baz &&
 
 	test_must_fail git checkout baz &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/baz &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout of branch from a single remote succeeds #3' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D spam &&
 
@@ -242,7 +242,7 @@
 '
 
 test_expect_success 'checkout of branch from a single remote succeeds #4' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D eggs &&
 
@@ -254,7 +254,7 @@
 '
 
 test_expect_success 'checkout of branch with a file having the same name fails' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D spam &&
 
@@ -262,11 +262,11 @@
 	test_must_fail git checkout spam &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/spam &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D spam &&
 
@@ -276,11 +276,11 @@
 	test_must_fail git -C sub checkout spam &&
 	status_uno_is_clean &&
 	test_must_fail git rev-parse --verify refs/heads/spam &&
-	test_branch master
+	test_branch main
 '
 
 test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
-	git checkout -B master &&
+	git checkout -B main &&
 	status_uno_is_clean &&
 	test_might_fail git branch -D spam &&
 
@@ -294,7 +294,7 @@
 
 test_expect_success 'loosely defined local base branch is reported correctly' '
 
-	git checkout master &&
+	git checkout main &&
 	status_uno_is_clean &&
 	git branch strict &&
 	git branch loose &&
@@ -302,8 +302,8 @@
 
 	test_config branch.strict.remote . &&
 	test_config branch.loose.remote . &&
-	test_config branch.strict.merge refs/heads/master &&
-	test_config branch.loose.merge master &&
+	test_config branch.strict.merge refs/heads/main &&
+	test_config branch.loose.merge main &&
 
 	git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
 	status_uno_is_clean &&
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index bcba1bf..4453741 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -2,6 +2,9 @@
 
 test_description='tests for git branch --track'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -10,14 +13,14 @@
 '
 
 test_expect_success 'checkout --track -b creates a new tracking branch' '
-	git checkout --track -b branch1 master &&
+	git checkout --track -b branch1 main &&
 	test $(git rev-parse --abbrev-ref HEAD) = branch1 &&
 	test $(git config --get branch.branch1.remote) = . &&
-	test $(git config --get branch.branch1.merge) = refs/heads/master
+	test $(git config --get branch.branch1.merge) = refs/heads/main
 '
 
 test_expect_success 'checkout --track -b rejects an extra path argument' '
-	test_must_fail git checkout --track -b branch2 master one.t 2>err &&
+	test_must_fail git checkout --track -b branch2 main one.t 2>err &&
 	test_i18ngrep "cannot be used with updating paths" err
 '
 
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
index 309199b..be6c84c 100755
--- a/t/t2030-unresolve-info.sh
+++ b/t/t2030-unresolve-info.sh
@@ -2,6 +2,9 @@
 
 test_description='undoing resolution'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_resolve_undo () {
@@ -59,7 +62,7 @@
 	test_commit fourth fi/le fourth &&
 	git checkout add-add &&
 	test_commit fifth add-differently &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'add records switch clears' '
@@ -183,8 +186,8 @@
 '
 
 test_expect_success 'rerere forget (add-add conflict)' '
-	git checkout -f master &&
-	echo master >add-differently &&
+	git checkout -f main &&
+	echo main >add-differently &&
 	git add add-differently &&
 	git commit -m "add differently" &&
 	test_must_fail git merge fifth &&
diff --git a/t/t2060-switch.sh b/t/t2060-switch.sh
index 68c9101..9bc6a3a 100755
--- a/t/t2060-switch.sh
+++ b/t/t2060-switch.sh
@@ -2,6 +2,9 @@
 
 test_description='switch basic functionality'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -23,41 +26,41 @@
 '
 
 test_expect_success 'switch and detach' '
-	test_when_finished git switch master &&
-	test_must_fail git switch master^{commit} &&
-	git switch --detach master^{commit} &&
+	test_when_finished git switch main &&
+	test_must_fail git switch main^{commit} &&
+	git switch --detach main^{commit} &&
 	test_must_fail git symbolic-ref HEAD
 '
 
 test_expect_success 'switch and detach current branch' '
-	test_when_finished git switch master &&
-	git switch master &&
+	test_when_finished git switch main &&
+	git switch main &&
 	git switch --detach &&
 	test_must_fail git symbolic-ref HEAD
 '
 
 test_expect_success 'switch and create branch' '
-	test_when_finished git switch master &&
-	git switch -c temp master^ &&
-	test_cmp_rev master^ refs/heads/temp &&
+	test_when_finished git switch main &&
+	git switch -c temp main^ &&
+	test_cmp_rev main^ refs/heads/temp &&
 	echo refs/heads/temp >expected-branch &&
 	git symbolic-ref HEAD >actual-branch &&
 	test_cmp expected-branch actual-branch
 '
 
 test_expect_success 'force create branch from HEAD' '
-	test_when_finished git switch master &&
-	git switch --detach master &&
+	test_when_finished git switch main &&
+	git switch --detach main &&
 	test_must_fail git switch -c temp &&
 	git switch -C temp &&
-	test_cmp_rev master refs/heads/temp &&
+	test_cmp_rev main refs/heads/temp &&
 	echo refs/heads/temp >expected-branch &&
 	git symbolic-ref HEAD >actual-branch &&
 	test_cmp expected-branch actual-branch
 '
 
 test_expect_success 'new orphan branch from empty' '
-	test_when_finished git switch master &&
+	test_when_finished git switch main &&
 	test_must_fail git switch --orphan new-orphan HEAD &&
 	git switch --orphan new-orphan &&
 	test_commit orphan &&
@@ -69,7 +72,7 @@
 '
 
 test_expect_success 'orphan branch works with --discard-changes' '
-	test_when_finished git switch master &&
+	test_when_finished git switch main &&
 	echo foo >foo.txt &&
 	git switch --discard-changes --orphan new-orphan2 &&
 	git ls-files >tracked-files &&
@@ -77,7 +80,7 @@
 '
 
 test_expect_success 'switching ignores file of same branch name' '
-	test_when_finished git switch master &&
+	test_when_finished git switch main &&
 	: >first-branch &&
 	git switch first-branch &&
 	echo refs/heads/first-branch >expected &&
@@ -86,7 +89,7 @@
 '
 
 test_expect_success 'guess and create branch' '
-	test_when_finished git switch master &&
+	test_when_finished git switch main &&
 	test_must_fail git switch --no-guess foo &&
 	test_config checkout.guess false &&
 	test_must_fail git switch foo &&
diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh
index 89e5a14..7c43ddf 100755
--- a/t/t2070-restore.sh
+++ b/t/t2070-restore.sh
@@ -2,6 +2,9 @@
 
 test_description='restore basic functionality'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -15,7 +18,7 @@
 	echo ignored >ignored &&
 	echo /ignored >.gitignore &&
 	git add one two .gitignore &&
-	git update-ref refs/heads/one master
+	git update-ref refs/heads/one main
 '
 
 test_expect_success 'restore without pathspec is not ok' '
@@ -91,7 +94,7 @@
 		git switch -c first &&
 		echo first >unmerged &&
 		git commit -am first &&
-		git switch -c second master &&
+		git switch -c second main &&
 		echo second >unmerged &&
 		git commit -am second &&
 		test_must_fail git merge first &&
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 7e2e7dd..30666fc 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -9,6 +9,11 @@
 
 sane_unset GIT_TEST_SPLIT_INDEX
 
+test_set_index_version () {
+    GIT_INDEX_VERSION="$1"
+    export GIT_INDEX_VERSION
+}
+
 test_set_index_version 3
 
 cat >expect.full <<EOF
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 7cb7a70..45ca35d 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -135,7 +135,7 @@
 	after=$(git ls-files -s check top) &&
 
 	test "$before" = "$after" &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 
 '
 
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index 5a74954..96dfca1 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -2,6 +2,9 @@
 
 test_description='test git worktree add'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -12,12 +15,12 @@
 
 test_expect_success '"add" an existing worktree' '
 	mkdir -p existing/subtree &&
-	test_must_fail git worktree add --detach existing master
+	test_must_fail git worktree add --detach existing main
 '
 
 test_expect_success '"add" an existing empty worktree' '
 	mkdir existing_empty &&
-	git worktree add --detach existing_empty master
+	git worktree add --detach existing_empty main
 '
 
 test_expect_success '"add" using shorthand - fails when no previous branch' '
@@ -29,7 +32,7 @@
 	echo hello >myworld &&
 	git add myworld &&
 	git commit -m myworld &&
-	git checkout master &&
+	git checkout main &&
 	git worktree add short-hand - &&
 	echo refs/heads/newbranch >expect &&
 	git -C short-hand rev-parse --symbolic-full-name HEAD >actual &&
@@ -37,7 +40,7 @@
 '
 
 test_expect_success '"add" refuses to checkout locked branch' '
-	test_must_fail git worktree add zere master &&
+	test_must_fail git worktree add zere main &&
 	! test -d zere &&
 	! test -d .git/worktrees/zere
 '
@@ -46,13 +49,13 @@
 	(
 	cd existing_empty &&
 	echo dirty >>init.t &&
-	git checkout master -- init.t
+	git checkout main -- init.t
 	)
 '
 
 test_expect_success '"add" worktree' '
 	git rev-parse HEAD >expect &&
-	git worktree add --detach here master &&
+	git worktree add --detach here main &&
 	(
 		cd here &&
 		test_cmp ../init.t init.t &&
@@ -65,7 +68,7 @@
 
 test_expect_success '"add" worktree with lock' '
 	git rev-parse HEAD >expect &&
-	git worktree add --detach --lock here-with-lock master &&
+	git worktree add --detach --lock here-with-lock main &&
 	test -f .git/worktrees/here-with-lock/locked
 '
 
@@ -73,7 +76,7 @@
 	(
 		mkdir sub &&
 		cd sub &&
-		git worktree add --detach here master &&
+		git worktree add --detach here main &&
 		cd here &&
 		test_cmp ../../init.t init.t
 	)
@@ -82,19 +85,19 @@
 test_expect_success '"add" from a linked checkout' '
 	(
 		cd here &&
-		git worktree add --detach nested-here master &&
+		git worktree add --detach nested-here main &&
 		cd nested-here &&
 		git fsck
 	)
 '
 
 test_expect_success '"add" worktree creating new branch' '
-	git worktree add -b newmaster there master &&
+	git worktree add -b newmain there main &&
 	(
 		cd there &&
 		test_cmp ../init.t init.t &&
 		git symbolic-ref HEAD >actual &&
-		echo refs/heads/newmaster >expect &&
+		echo refs/heads/newmain >expect &&
 		test_cmp expect actual &&
 		git fsck
 	)
@@ -103,7 +106,7 @@
 test_expect_success 'die the same branch is already checked out' '
 	(
 		cd here &&
-		test_must_fail git checkout newmaster
+		test_must_fail git checkout newmain
 	)
 '
 
@@ -112,20 +115,20 @@
 	ref=$(git -C there symbolic-ref HEAD) &&
 	rm "$head" &&
 	ln -s "$ref" "$head" &&
-	test_must_fail git -C here checkout newmaster
+	test_must_fail git -C here checkout newmain
 '
 
 test_expect_success 'not die the same branch is already checked out' '
 	(
 		cd here &&
-		git worktree add --force anothernewmaster newmaster
+		git worktree add --force anothernewmain newmain
 	)
 '
 
 test_expect_success 'not die on re-checking out current branch' '
 	(
 		cd there &&
-		git checkout newmaster
+		git checkout newmain
 	)
 '
 
@@ -133,14 +136,14 @@
 	(
 		git clone --bare . bare &&
 		cd bare &&
-		git worktree add -b bare-master ../there2 master
+		git worktree add -b bare-main ../there2 main
 	)
 '
 
 test_expect_success 'checkout from a bare repo without "add"' '
 	(
 		cd bare &&
-		test_must_fail git checkout master
+		test_must_fail git checkout main
 	)
 '
 
@@ -148,7 +151,7 @@
 	(
 		git clone --bare . bare2 &&
 		cd bare2 &&
-		git worktree add ../there3 master
+		git worktree add ../there3 main
 	)
 '
 
@@ -165,7 +168,7 @@
 	EOF
 	git log --format=%s -2 >actual &&
 	test_cmp expected actual &&
-	git worktree add --detach grafted master &&
+	git worktree add --detach grafted main &&
 	git --git-dir=grafted/.git log --format=%s -2 >actual &&
 	test_cmp expected actual
 '
@@ -226,34 +229,34 @@
 '
 
 test_expect_success '"add" -b/-B mutually exclusive' '
-	test_must_fail git worktree add -b poodle -B poodle bamboo master
+	test_must_fail git worktree add -b poodle -B poodle bamboo main
 '
 
 test_expect_success '"add" -b/--detach mutually exclusive' '
-	test_must_fail git worktree add -b poodle --detach bamboo master
+	test_must_fail git worktree add -b poodle --detach bamboo main
 '
 
 test_expect_success '"add" -B/--detach mutually exclusive' '
-	test_must_fail git worktree add -B poodle --detach bamboo master
+	test_must_fail git worktree add -B poodle --detach bamboo main
 '
 
 test_expect_success '"add -B" fails if the branch is checked out' '
-	git rev-parse newmaster >before &&
-	test_must_fail git worktree add -B newmaster bamboo master &&
-	git rev-parse newmaster >after &&
+	git rev-parse newmain >before &&
+	test_must_fail git worktree add -B newmain bamboo main &&
+	git rev-parse newmain >after &&
 	test_cmp before after
 '
 
 test_expect_success 'add -B' '
-	git worktree add -B poodle bamboo2 master^ &&
+	git worktree add -B poodle bamboo2 main^ &&
 	git -C bamboo2 symbolic-ref HEAD >actual &&
 	echo refs/heads/poodle >expected &&
 	test_cmp expected actual &&
-	test_cmp_rev master^ poodle
+	test_cmp_rev main^ poodle
 '
 
 test_expect_success 'add --quiet' '
-	git worktree add --quiet another-worktree master 2>actual &&
+	git worktree add --quiet another-worktree main 2>actual &&
 	test_must_be_empty actual
 '
 
@@ -348,24 +351,24 @@
 
 test_expect_success '--track sets up tracking' '
 	test_when_finished rm -rf track &&
-	git worktree add --track -b track track master &&
-	test_branch_upstream track . master
+	git worktree add --track -b track track main &&
+	test_branch_upstream track . main
 '
 
 # setup remote repository $1 and repository $2 with $1 set up as
-# remote.  The remote has two branches, master and foo.
+# remote.  The remote has two branches, main and foo.
 setup_remote_repo () {
 	git init $1 &&
 	(
 		cd $1 &&
-		test_commit $1_master &&
+		test_commit $1_main &&
 		git checkout -b foo &&
 		test_commit upstream_foo
 	) &&
 	git init $2 &&
 	(
 		cd $2 &&
-		test_commit $2_master &&
+		test_commit $2_main &&
 		git remote add $1 ../$1 &&
 		git config remote.$1.fetch \
 			"refs/heads/*:refs/remotes/$1/*" &&
diff --git a/t/t2401-worktree-prune.sh b/t/t2401-worktree-prune.sh
index a6ce7f5..a615d3b 100755
--- a/t/t2401-worktree-prune.sh
+++ b/t/t2401-worktree-prune.sh
@@ -2,6 +2,9 @@
 
 test_description='prune $GIT_DIR/worktrees'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success initialize '
@@ -20,7 +23,7 @@
 	cat >expect <<EOF &&
 Removing worktrees/abc: not a valid directory
 EOF
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	! test -f .git/worktrees/abc &&
 	! test -d .git/worktrees
 '
@@ -32,7 +35,7 @@
 Removing worktrees/def: gitdir file does not exist
 EOF
 	git worktree prune --verbose >actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	! test -d .git/worktrees/def &&
 	! test -d .git/worktrees
 '
@@ -87,7 +90,7 @@
 
 test_expect_success 'not prune proper checkouts' '
 	test_when_finished rm -r .git/worktrees &&
-	git worktree add --detach "$PWD/nop" master &&
+	git worktree add --detach "$PWD/nop" main &&
 	git worktree prune &&
 	test -d .git/worktrees/nop
 '
diff --git a/t/t2402-worktree-list.sh b/t/t2402-worktree-list.sh
index 795ddca..fedcefe 100755
--- a/t/t2402-worktree-list.sh
+++ b/t/t2402-worktree-list.sh
@@ -2,6 +2,9 @@
 
 test_description='test git worktree list'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -21,7 +24,7 @@
 test_expect_success 'rev-parse --git-path objects linked worktree' '
 	echo "$(git rev-parse --show-toplevel)/.git/objects" >expect &&
 	test_when_finished "rm -rf linked-tree actual expect && git worktree prune" &&
-	git worktree add --detach linked-tree master &&
+	git worktree add --detach linked-tree main &&
 	git -C linked-tree rev-parse --git-path objects >actual &&
 	test_cmp expect actual
 '
@@ -29,7 +32,7 @@
 test_expect_success '"list" all worktrees from main' '
 	echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
 	test_when_finished "rm -rf here out actual expect && git worktree prune" &&
-	git worktree add --detach here master &&
+	git worktree add --detach here main &&
 	echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
 	git worktree list >out &&
 	sed "s/  */ /g" <out >actual &&
@@ -39,7 +42,7 @@
 test_expect_success '"list" all worktrees from linked' '
 	echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
 	test_when_finished "rm -rf here out actual expect && git worktree prune" &&
-	git worktree add --detach here master &&
+	git worktree add --detach here main &&
 	echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
 	git -C here worktree list >out &&
 	sed "s/  */ /g" <out >actual &&
@@ -52,7 +55,7 @@
 	echo "branch $(git symbolic-ref HEAD)" >>expect &&
 	echo >>expect &&
 	test_when_finished "rm -rf here actual expect && git worktree prune" &&
-	git worktree add --detach here master &&
+	git worktree add --detach here main &&
 	echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
 	echo "HEAD $(git rev-parse HEAD)" >>expect &&
 	echo "detached" >>expect &&
@@ -63,26 +66,122 @@
 
 test_expect_success '"list" all worktrees with locked annotation' '
 	test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
-	git worktree add --detach locked master &&
-	git worktree add --detach unlocked master &&
+	git worktree add --detach locked main &&
+	git worktree add --detach unlocked main &&
 	git worktree lock locked &&
+	test_when_finished "git worktree unlock locked" &&
 	git worktree list >out &&
 	grep "/locked  *[0-9a-f].* locked$" out &&
 	! grep "/unlocked  *[0-9a-f].* locked$" out
 '
 
+test_expect_success '"list" all worktrees --porcelain with locked' '
+	test_when_finished "rm -rf locked1 locked2 unlocked out actual expect && git worktree prune" &&
+	echo "locked" >expect &&
+	echo "locked with reason" >>expect &&
+	git worktree add --detach locked1 &&
+	git worktree add --detach locked2 &&
+	# unlocked worktree should not be annotated with "locked"
+	git worktree add --detach unlocked &&
+	git worktree lock locked1 &&
+	test_when_finished "git worktree unlock locked1" &&
+	git worktree lock locked2 --reason "with reason" &&
+	test_when_finished "git worktree unlock locked2" &&
+	git worktree list --porcelain >out &&
+	grep "^locked" out >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees --porcelain with locked reason newline escaped' '
+	test_when_finished "rm -rf locked_lf locked_crlf out actual expect && git worktree prune" &&
+	printf "locked \"locked\\\\r\\\\nreason\"\n" >expect &&
+	printf "locked \"locked\\\\nreason\"\n" >>expect &&
+	git worktree add --detach locked_lf &&
+	git worktree add --detach locked_crlf &&
+	git worktree lock locked_lf --reason "$(printf "locked\nreason")" &&
+	test_when_finished "git worktree unlock locked_lf" &&
+	git worktree lock locked_crlf --reason "$(printf "locked\r\nreason")" &&
+	test_when_finished "git worktree unlock locked_crlf" &&
+	git worktree list --porcelain >out &&
+	grep "^locked" out >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '"list" all worktrees with prunable annotation' '
+	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	git worktree add --detach unprunable &&
+	rm -rf prunable &&
+	git worktree list >out &&
+	grep "/prunable  *[0-9a-f].* prunable$" out &&
+	! grep "/unprunable  *[0-9a-f].* prunable$"
+'
+
+test_expect_success '"list" all worktrees --porcelain with prunable' '
+	test_when_finished "rm -rf prunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	rm -rf prunable &&
+	git worktree list --porcelain >out &&
+	sed -n "/^worktree .*\/prunable$/,/^$/p" <out >only_prunable &&
+	test_i18ngrep "^prunable gitdir file points to non-existent location$" only_prunable
+'
+
+test_expect_success '"list" all worktrees with prunable consistent with "prune"' '
+	test_when_finished "rm -rf prunable unprunable out && git worktree prune" &&
+	git worktree add --detach prunable &&
+	git worktree add --detach unprunable &&
+	rm -rf prunable &&
+	git worktree list >out &&
+	grep "/prunable  *[0-9a-f].* prunable$" out &&
+	! grep "/unprunable  *[0-9a-f].* unprunable$" out &&
+	git worktree prune --verbose >out &&
+	test_i18ngrep "^Removing worktrees/prunable" out &&
+	test_i18ngrep ! "^Removing worktrees/unprunable" out
+'
+
+test_expect_success '"list" --verbose and --porcelain mutually exclusive' '
+	test_must_fail git worktree list --verbose --porcelain
+'
+
+test_expect_success '"list" all worktrees --verbose with locked' '
+	test_when_finished "rm -rf locked1 locked2 out actual expect && git worktree prune" &&
+	git worktree add locked1 --detach &&
+	git worktree add locked2 --detach &&
+	git worktree lock locked1 &&
+	test_when_finished "git worktree unlock locked1" &&
+	git worktree lock locked2 --reason "with reason" &&
+	test_when_finished "git worktree unlock locked2" &&
+	echo "$(git -C locked2 rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
+	printf "\tlocked: with reason\n" >>expect &&
+	git worktree list --verbose >out &&
+	grep "/locked1  *[0-9a-f].* locked$" out &&
+	sed -n "s/  */ /g;/\/locked2  *[0-9a-f].*$/,/locked: .*$/p" <out >actual &&
+	test_cmp actual expect
+'
+
+test_expect_success '"list" all worktrees --verbose with prunable' '
+	test_when_finished "rm -rf prunable out actual expect && git worktree prune" &&
+	git worktree add prunable --detach &&
+	echo "$(git -C prunable rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >expect &&
+	printf "\tprunable: gitdir file points to non-existent location\n" >>expect &&
+	rm -rf prunable &&
+	git worktree list --verbose >out &&
+	sed -n "s/  */ /g;/\/prunable  *[0-9a-f].*$/,/prunable: .*$/p" <out >actual &&
+	test_cmp actual expect
+'
+
 test_expect_success 'bare repo setup' '
 	git init --bare bare1 &&
 	echo "data" >file1 &&
 	git add file1 &&
 	git commit -m"File1: add data" &&
-	git push bare1 master &&
+	git push bare1 main &&
 	git reset --hard HEAD^
 '
 
 test_expect_success '"list" all worktrees from bare main' '
 	test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
-	git -C bare1 worktree add --detach ../there master &&
+	git -C bare1 worktree add --detach ../there main &&
 	echo "$(pwd)/bare1 (bare)" >expect &&
 	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
 	git -C bare1 worktree list >out &&
@@ -92,7 +191,7 @@
 
 test_expect_success '"list" all worktrees --porcelain from bare main' '
 	test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" &&
-	git -C bare1 worktree add --detach ../there master &&
+	git -C bare1 worktree add --detach ../there main &&
 	echo "worktree $(pwd)/bare1" >expect &&
 	echo "bare" >>expect &&
 	echo >>expect &&
@@ -106,7 +205,7 @@
 
 test_expect_success '"list" all worktrees from linked with a bare main' '
 	test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
-	git -C bare1 worktree add --detach ../there master &&
+	git -C bare1 worktree add --detach ../there main &&
 	echo "$(pwd)/bare1 (bare)" >expect &&
 	echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
 	git -C there worktree list >out &&
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index e1b2bfd..b172c26 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -2,6 +2,9 @@
 
 test_description='Combination of submodules and multiple worktrees'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 base_path=$(pwd -P)
@@ -31,7 +34,7 @@
 '
 
 test_expect_failure 'submodule is checked out just after worktree add' '
-	git -C worktree diff --submodule master"^!" >out &&
+	git -C worktree diff --submodule main"^!" >out &&
 	grep "file1 updated" out
 '
 
@@ -41,7 +44,7 @@
 '
 
 test_expect_success 'submodule is checked out just after submodule update in linked worktree' '
-	git -C worktree-submodule-update diff --submodule master"^!" >out &&
+	git -C worktree-submodule-update diff --submodule main"^!" >out &&
 	grep "file1 updated" out
 '
 
@@ -51,7 +54,7 @@
 '
 
 test_expect_success 'submodule is checked out after manually adding submodule worktree' '
-	git -C linked_submodule diff --submodule master"^!" >out &&
+	git -C linked_submodule diff --submodule main"^!" >out &&
 	grep "file1 updated" out
 '
 
diff --git a/t/t2406-worktree-repair.sh b/t/t2406-worktree-repair.sh
index 1fe468b..f737418 100755
--- a/t/t2406-worktree-repair.sh
+++ b/t/t2406-worktree-repair.sh
@@ -104,6 +104,16 @@
 	test_i18ngrep ".git is not a file" err
 '
 
+test_expect_success 'repo not found; .git not referencing repo' '
+	test_when_finished "rm -rf side not-a-repo && git worktree prune" &&
+	git worktree add --detach side &&
+	sed s,\.git/worktrees/side$,not-a-repo, side/.git >side/.newgit &&
+	mv side/.newgit side/.git &&
+	mkdir not-a-repo &&
+	test_must_fail git worktree repair side 2>err &&
+	test_i18ngrep ".git file does not reference a repository" err
+'
+
 test_expect_success 'repo not found; .git file broken' '
 	test_when_finished "rm -rf orig moved && git worktree prune" &&
 	git worktree add --detach orig &&
@@ -176,4 +186,20 @@
 	test_must_be_empty err
 '
 
+test_expect_success 'repair moved main and linked worktrees' '
+	test_when_finished "rm -rf main side mainmoved sidemoved" &&
+	test_create_repo main &&
+	test_commit -C main init &&
+	git -C main worktree add --detach ../side &&
+	sed "s,side/\.git$,sidemoved/.git," \
+		main/.git/worktrees/side/gitdir >expect-gitdir &&
+	sed "s,main/.git/worktrees/side$,mainmoved/.git/worktrees/side," \
+		side/.git >expect-gitfile &&
+	mv main mainmoved &&
+	mv side sidemoved &&
+	git -C mainmoved worktree repair ../sidemoved &&
+	test_cmp expect-gitdir mainmoved/.git/worktrees/side/gitdir &&
+	test_cmp expect-gitfile sidemoved/.git
+'
+
 test_done
diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh
index 2ec69a8..727e9ae 100755
--- a/t/t3005-ls-files-relative.sh
+++ b/t/t3005-ls-files-relative.sh
@@ -46,7 +46,7 @@
 		ls ../x* >expect.out &&
 		test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
 		test_cmp expect.out actual.out &&
-		test_i18ncmp expect.err actual.err
+		test_cmp expect.err actual.err
 	)
 '
 
@@ -61,7 +61,7 @@
 		ls ../y* >expect.out &&
 		test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
 		test_cmp expect.out actual.out &&
-		test_i18ncmp expect.err actual.err
+		test_cmp expect.err actual.err
 	)
 '
 
diff --git a/t/t3012-ls-files-dedup.sh b/t/t3012-ls-files-dedup.sh
new file mode 100755
index 0000000..2682b1f
--- /dev/null
+++ b/t/t3012-ls-files-dedup.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git ls-files --deduplicate test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	>a.txt &&
+	>b.txt &&
+	>delete.txt &&
+	git add a.txt b.txt delete.txt &&
+	git commit -m base &&
+	echo a >a.txt &&
+	echo b >b.txt &&
+	echo delete >delete.txt &&
+	git add a.txt b.txt delete.txt &&
+	git commit -m tip &&
+	git tag tip &&
+	git reset --hard HEAD^ &&
+	echo change >a.txt &&
+	git commit -a -m side &&
+	git tag side
+'
+
+test_expect_success 'git ls-files --deduplicate to show unique unmerged path' '
+	test_must_fail git merge tip &&
+	git ls-files --deduplicate >actual &&
+	cat >expect <<-\EOF &&
+	a.txt
+	b.txt
+	delete.txt
+	EOF
+	test_cmp expect actual &&
+	git merge --abort
+'
+
+test_expect_success 'git ls-files -d -m --deduplicate with different display options' '
+	git reset --hard side &&
+	test_must_fail git merge tip &&
+	rm delete.txt &&
+	git ls-files -d -m --deduplicate >actual &&
+	cat >expect <<-\EOF &&
+	a.txt
+	delete.txt
+	EOF
+	test_cmp expect actual &&
+	git ls-files -d -m -t --deduplicate >actual &&
+	cat >expect <<-\EOF &&
+	C a.txt
+	C a.txt
+	C a.txt
+	R delete.txt
+	C delete.txt
+	EOF
+	test_cmp expect actual &&
+	git ls-files -d -m -c --deduplicate >actual &&
+	cat >expect <<-\EOF &&
+	a.txt
+	b.txt
+	delete.txt
+	EOF
+	test_cmp expect actual &&
+	git merge --abort
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 3ec3e1d..cc4b102 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -5,6 +5,9 @@
 
 test_description='git branch assorted tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
@@ -306,7 +309,9 @@
 
 	git branch -v --list --no-abbrev t >actual.noabbrev &&
 	git branch -v --list --abbrev=0 t >actual.0abbrev &&
+	git -c core.abbrev=no branch -v --list t >actual.noabbrev-conf &&
 	test_cmp actual.noabbrev actual.0abbrev &&
+	test_cmp actual.noabbrev actual.noabbrev-conf &&
 
 	git branch -v --list --abbrev=36 t >actual.36abbrev &&
 	# how many hexdigits are used?
@@ -690,7 +695,7 @@
 	git branch -d symref >actual &&
 	test_path_is_file .git/refs/heads/target &&
 	test_path_is_missing .git/refs/heads/symref &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'deleting a dangling symref' '
@@ -699,7 +704,7 @@
 	echo "Deleted branch dangling-symref (was nowhere)." >expect &&
 	git branch -d dangling-symref >actual &&
 	test_path_is_missing .git/refs/heads/dangling-symref &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'deleting a self-referential symref' '
@@ -708,7 +713,7 @@
 	echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
 	git branch -d self-reference >actual &&
 	test_path_is_missing .git/refs/heads/self-reference &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'renaming a symref is not allowed' '
@@ -803,7 +808,7 @@
 	sha1=$(git rev-parse my7 | cut -c 1-7) &&
 	echo "Deleted branch my7 (was $sha1)." >expect &&
 	git branch -d my7 >actual 2>&1 &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'deleting currently checked out branch fails' '
@@ -838,7 +843,7 @@
 test_expect_success '--set-upstream-to fails on multiple branches' '
 	echo "fatal: too many arguments to set new upstream" >expect &&
 	test_must_fail git branch --set-upstream-to main a b c 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on detached HEAD' '
@@ -846,13 +851,13 @@
 	test_when_finished git checkout - &&
 	echo "fatal: could not set upstream of HEAD to main when it does not point to any branch." >expect &&
 	test_must_fail git branch --set-upstream-to main 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on a missing dst branch' '
 	echo "fatal: branch '"'"'does-not-exist'"'"' does not exist" >expect &&
 	test_must_fail git branch --set-upstream-to main does-not-exist 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on a missing src branch' '
@@ -863,7 +868,7 @@
 test_expect_success '--set-upstream-to fails on a non-ref' '
 	echo "fatal: Cannot setup tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch." >expect &&
 	test_must_fail git branch --set-upstream-to HEAD^{} 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on locked config' '
@@ -894,7 +899,7 @@
 test_expect_success '--unset-upstream should fail if given a non-existent branch' '
 	echo "fatal: Branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
 	test_must_fail git branch --unset-upstream i-dont-exist 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail if config is locked' '
@@ -916,13 +921,13 @@
 	# fail for a branch without upstream set
 	echo "fatal: Branch '"'"'main'"'"' has no upstream information" >expect &&
 	test_must_fail git branch --unset-upstream 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail on multiple branches' '
 	echo "fatal: too many arguments to unset upstream" >expect &&
 	test_must_fail git branch --unset-upstream a b c 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail on detached HEAD' '
@@ -930,7 +935,7 @@
 	test_when_finished git checkout - &&
 	echo "fatal: could not unset upstream of HEAD when it does not point to any branch." >expect &&
 	test_must_fail git branch --unset-upstream 2>err &&
-	test_i18ncmp expect err
+	test_cmp expect err
 '
 
 test_expect_success 'test --unset-upstream on a particular branch' '
@@ -952,7 +957,7 @@
 	EOF
 	test_expect_code 1 git config branch.my13.remote &&
 	test_expect_code 1 git config branch.my13.merge &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 # Keep this test last, as it changes the current branch
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
index 6c1ab69..349a810 100755
--- a/t/t3201-branch-contains.sh
+++ b/t/t3201-branch-contains.sh
@@ -2,6 +2,9 @@
 
 test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -261,7 +264,7 @@
 	* topic $(git rev-parse --short topic ) [ahead 1] foo
 	  zzz   $(git rev-parse --short zzz   ) second on main
 	EOF
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t3202-show-branch-octopus.sh b/t/t3202-show-branch-octopus.sh
index 6adf478..5cb0126 100755
--- a/t/t3202-show-branch-octopus.sh
+++ b/t/t3202-show-branch-octopus.sh
@@ -2,6 +2,9 @@
 
 test_description='test show-branch with more than 8 heads'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 numbers="1 2 3 4 5 6 7 8 9 10"
@@ -15,7 +18,7 @@
 
 	for i in $numbers
 	do
-		git checkout -b branch$i master &&
+		git checkout -b branch$i main &&
 		> file$i &&
 		git add file$i &&
 		test_tick &&
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 5e0577d..5325b9f 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git branch display tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -157,7 +160,7 @@
 EOF
 	git checkout HEAD^0 &&
 	git branch >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after checkout --detach' '
@@ -170,7 +173,7 @@
 EOF
 	git checkout --detach &&
 	git branch >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after moving' '
@@ -182,7 +185,7 @@
 EOF
 	git reset --hard HEAD^1 &&
 	git branch >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly from tag' '
@@ -195,7 +198,7 @@
 	git tag fromtag main &&
 	git checkout fromtag &&
 	git branch >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after moving from tag' '
@@ -207,7 +210,7 @@
 EOF
 	git reset --hard HEAD^1 &&
 	git branch >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]objectsize` option' '
@@ -218,7 +221,7 @@
 	  main
 	EOF
 	git branch --sort=objectsize >actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 
 	cat >expect <<-\EOF &&
 	* (HEAD detached from fromtag)
@@ -227,7 +230,7 @@
 	  branch-two
 	EOF
 	git branch --sort=-objectsize >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]type` option' '
@@ -238,7 +241,7 @@
 	  main
 	EOF
 	git branch --sort=type >actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 
 	cat >expect <<-\EOF &&
 	* (HEAD detached from fromtag)
@@ -247,7 +250,7 @@
 	  main
 	EOF
 	git branch --sort=-type >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]version:refname` option' '
@@ -258,7 +261,7 @@
 	  main
 	EOF
 	git branch --sort=version:refname >actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 
 	cat >expect <<-\EOF &&
 	* (HEAD detached from fromtag)
@@ -267,7 +270,7 @@
 	  branch-one
 	EOF
 	git branch --sort=-version:refname >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git branch --points-at option' '
@@ -334,7 +337,7 @@
 	Refname is refs/heads/ref-to-remote
 	EOF
 	git branch --format="Refname is %(refname)" >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'worktree colors correct' '
@@ -352,7 +355,7 @@
 	rm -r worktree_dir &&
 	git worktree prune &&
 	test_decode_color <actual.raw >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success "set up color tests" '
@@ -395,7 +398,7 @@
 	git branch -vv >actual &&
 	rm -r worktree_dir &&
 	git worktree prune &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
index 698d9cc..993a6b5 100755
--- a/t/t3204-branch-name-interpretation.sh
+++ b/t/t3204-branch-name-interpretation.sh
@@ -6,6 +6,9 @@
 refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc.
 This script aims to check the behavior of those corner cases.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 expect_branch() {
@@ -28,7 +31,7 @@
 	git branch previous one &&
 
 	git checkout previous &&
-	git checkout master &&
+	git checkout main &&
 
 	git branch -f @{-1} two &&
 	expect_branch previous two
@@ -58,7 +61,7 @@
 	git branch previous-del &&
 
 	git checkout previous-del &&
-	git checkout master &&
+	git checkout main &&
 
 	git branch -D @{-1} &&
 	expect_deleted previous-del
@@ -98,7 +101,7 @@
 	git update-ref refs/remotes/origin/previous one &&
 
 	git checkout -b origin/previous two &&
-	git checkout master &&
+	git checkout main &&
 
 	test_must_fail git branch -r -D @{-1} &&
 	expect_branch refs/remotes/origin/previous one &&
diff --git a/t/t3205-branch-color.sh b/t/t3205-branch-color.sh
index da1c202..08bd906 100755
--- a/t/t3205-branch-color.sh
+++ b/t/t3205-branch-color.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='basic branch output coloring'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'set up some sample branches' '
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh
index 6eb344b..1b26c4c 100755
--- a/t/t3206-range-diff.sh
+++ b/t/t3206-range-diff.sh
@@ -2,6 +2,9 @@
 
 test_description='range-diff tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Note that because of the range-diff's heuristics, test_commit does more
@@ -127,7 +130,7 @@
 '
 
 test_expect_success 'simple A..B A..C (unmodified)' '
-	git range-diff --no-color master..topic master..unmodified \
+	git range-diff --no-color main..topic main..unmodified \
 		>actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/
@@ -145,13 +148,26 @@
 '
 
 test_expect_success 'simple A B C (unmodified)' '
-	git range-diff --no-color master topic unmodified >actual &&
+	git range-diff --no-color main topic unmodified >actual &&
 	# same "expect" as above
 	test_cmp expect actual
 '
 
+test_expect_success 'A^! and A^-<n> (unmodified)' '
+	git range-diff --no-color topic^! unmodified^-1 >actual &&
+	cat >expect <<-EOF &&
+	1:  $(test_oid t4) = 1:  $(test_oid u4) s/12/B/
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'A^{/..} is not mistaken for a range' '
+	test_must_fail git range-diff topic^.. topic^{/..} 2>error &&
+	test_i18ngrep "not a commit range" error
+'
+
 test_expect_success 'trivial reordering' '
-	git range-diff --no-color master topic reordered >actual &&
+	git range-diff --no-color main topic reordered >actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid r1) s/5/A/
 	3:  $(test_oid t3) = 2:  $(test_oid r2) s/11/B/
@@ -162,7 +178,7 @@
 '
 
 test_expect_success 'removed a commit' '
-	git range-diff --no-color master topic removed >actual &&
+	git range-diff --no-color main topic removed >actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid d1) s/5/A/
 	2:  $(test_oid t2) < -:  $(test_oid __) s/4/A/
@@ -173,7 +189,7 @@
 '
 
 test_expect_success 'added a commit' '
-	git range-diff --no-color master topic added >actual &&
+	git range-diff --no-color main topic added >actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid a1) s/5/A/
 	2:  $(test_oid t2) = 2:  $(test_oid a2) s/4/A/
@@ -185,7 +201,7 @@
 '
 
 test_expect_success 'new base, A B C' '
-	git range-diff --no-color master topic rebased >actual &&
+	git range-diff --no-color main topic rebased >actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid b1) s/5/A/
 	2:  $(test_oid t2) = 2:  $(test_oid b2) s/4/A/
@@ -196,7 +212,7 @@
 '
 
 test_expect_success 'new base, B...C' '
-	# this syntax includes the commits from master!
+	# this syntax includes the commits from main!
 	git range-diff --no-color topic...rebased >actual &&
 	cat >expect <<-EOF &&
 	-:  $(test_oid __) > 1:  $(test_oid b5) unrelated
@@ -420,7 +436,7 @@
 
 test_expect_success 'no commits on one side' '
 	git commit --amend -m "new message" &&
-	git range-diff master HEAD@{1} HEAD
+	git range-diff main HEAD@{1} HEAD
 '
 
 test_expect_success 'changed message' '
@@ -482,11 +498,11 @@
 	test_cmp expect actual
 '
 
-for prev in topic master..topic
+for prev in topic main..topic
 do
 	test_expect_success "format-patch --range-diff=$prev" '
 		git format-patch --cover-letter --range-diff=$prev \
-			master..unmodified >actual &&
+			main..unmodified >actual &&
 		test_when_finished "rm 000?-*" &&
 		test_line_count = 5 actual &&
 		test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -511,19 +527,19 @@
 
 test_expect_success 'basic with modified format.pretty with suffix' '
 	git -c format.pretty="format:commit %H%d%n" range-diff \
-		master..topic master..unmodified
+		main..topic main..unmodified
 '
 
 test_expect_success 'basic with modified format.pretty without "commit "' '
 	git -c format.pretty="format:%H%n" range-diff \
-		master..topic master..unmodified
+		main..topic main..unmodified
 '
 
 test_expect_success 'range-diff compares notes by default' '
 	git notes add -m "topic note" topic &&
 	git notes add -m "unmodified note" unmodified &&
 	test_when_finished git notes remove topic unmodified &&
-	git range-diff --no-color master..topic master..unmodified \
+	git range-diff --no-color main..topic main..unmodified \
 		>actual &&
 	sed s/Z/\ /g >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/
@@ -547,7 +563,7 @@
 	git notes add -m "topic note" topic &&
 	git notes add -m "unmodified note" unmodified &&
 	test_when_finished git notes remove topic unmodified &&
-	git range-diff --no-color --no-notes master..topic master..unmodified \
+	git range-diff --no-color --no-notes main..topic main..unmodified \
 		>actual &&
 	cat >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/
@@ -565,7 +581,7 @@
 	git notes --ref=note2 add -m "topic note2" topic &&
 	git notes --ref=note2 add -m "unmodified note2" unmodified &&
 	test_when_finished git notes --ref=note2 remove topic unmodified &&
-	git range-diff --no-color --notes=note1 --notes=note2 master..topic master..unmodified \
+	git range-diff --no-color --notes=note1 --notes=note2 main..topic main..unmodified \
 		>actual &&
 	sed s/Z/\ /g >expect <<-EOF &&
 	1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/
@@ -595,7 +611,7 @@
 	git notes add -m "unmodified note" unmodified &&
 	test_when_finished git notes remove topic unmodified &&
 	git format-patch --cover-letter --range-diff=$prev \
-		master..unmodified >actual &&
+		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
 	test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -612,7 +628,7 @@
 	git notes add -m "unmodified note" unmodified &&
 	test_when_finished git notes remove topic unmodified &&
 	git format-patch --no-notes --cover-letter --range-diff=$prev \
-		master..unmodified >actual &&
+		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
 	test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -629,7 +645,7 @@
 	git notes add -m "unmodified note" unmodified &&
 	test_when_finished git notes remove topic unmodified &&
 	git format-patch --notes --cover-letter --range-diff=$prev \
-		master..unmodified >actual &&
+		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
 	test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -658,7 +674,7 @@
 	test_when_finished git notes remove topic unmodified &&
 	test_config format.notes true &&
 	git format-patch --cover-letter --range-diff=$prev \
-		master..unmodified >actual &&
+		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
 	test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -689,7 +705,7 @@
 	git notes --ref=note2 add -m "unmodified note2" unmodified &&
 	test_when_finished git notes --ref=note2 remove topic unmodified &&
 	git format-patch --notes=note1 --notes=note2 --cover-letter --range-diff=$prev \
-		master..unmodified >actual &&
+		main..unmodified >actual &&
 	test_when_finished "rm 000?-*" &&
 	test_line_count = 5 actual &&
 	test_i18ngrep "^Range-diff:$" 0000-* &&
@@ -717,4 +733,19 @@
 	test_cmp expect actual
 '
 
+test_expect_success '--left-only/--right-only' '
+	git switch --orphan left-right &&
+	test_commit first &&
+	test_commit unmatched &&
+	test_commit common &&
+	git switch -C left-right first &&
+	git cherry-pick common &&
+
+	git range-diff -s --left-only ...common >actual &&
+	head_oid=$(git rev-parse --short HEAD) &&
+	common_oid=$(git rev-parse --short common) &&
+	echo "1:  $head_oid = 2:  $common_oid common" >expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t3206/history.export b/t/t3206/history.export
index 4c808e5..4485adc 100644
--- a/t/t3206/history.export
+++ b/t/t3206/history.export
@@ -168,7 +168,7 @@
 data 10
 unrelated
 
-commit refs/heads/master
+commit refs/heads/main
 mark :12
 author Thomas Rast <trast@inf.ethz.ch> 1374485127 +0200
 committer Thomas Rast <trast@inf.ethz.ch> 1374485127 +0200
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index f41b2af..3b7cdc5 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -9,6 +9,9 @@
 This test runs git pack-refs and git show-ref and checks that the branch
 semantic is still the same.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'enable reflogs' '
@@ -135,7 +138,7 @@
 	git commit --allow-empty -m "future garbage" &&
 	git pack-refs --all &&
 	git reset --hard HEAD^ &&
-	git checkout master &&
+	git checkout main &&
 	git reflog expire --expire=all --all &&
 	git prune --expire=all &&
 	git branch -d lamb 2>result &&
@@ -240,7 +243,7 @@
 
 test_expect_success SYMLINKS 'pack symlinked packed-refs' '
 	# First make sure that symlinking works when reading:
-	git update-ref refs/heads/lossy refs/heads/master &&
+	git update-ref refs/heads/lossy refs/heads/main &&
 	git for-each-ref >all-refs-before &&
 	mv .git/packed-refs .git/my-deviant-packed-refs &&
 	ln -s my-deviant-packed-refs .git/packed-refs &&
diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh
index 3b7caca..37b9d26 100755
--- a/t/t3211-peel-ref.sh
+++ b/t/t3211-peel-ref.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='tests for the peel_ref optimization of packed-refs'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'create annotated tag in refs/tags' '
@@ -19,7 +22,7 @@
 
 test_expect_success 'set up expected show-ref output' '
 	{
-		print_ref "refs/heads/master" &&
+		print_ref "refs/heads/main" &&
 		print_ref "refs/outside/foo" &&
 		print_ref "refs/outside/foo^{}" &&
 		print_ref "refs/tags/base" &&
@@ -47,7 +50,7 @@
 	# know we are emulating exactly what an older git would have written.
 	{
 		echo "# pack-refs with: peeled " &&
-		print_ref "refs/heads/master" &&
+		print_ref "refs/heads/main" &&
 		print_ref "refs/outside/foo" &&
 		print_ref "refs/tags/base" &&
 		print_ref "refs/tags/foo" &&
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index 04de03c..f5bf16a 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -181,7 +181,7 @@
 	git diff-index -M -p $t0 >diff &&
 	git apply --stat <diff >diffstat &&
 	sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current &&
-	test_i18ncmp expected current
+	test_cmp expected current
 '
 
 test_expect_success 'numstat for rename quotes funny filename' '
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index ca60faf..d742be8 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -1293,18 +1293,18 @@
 	grep "replacement note 3" actual
 '
 
-test_expect_success 'git notes copy diagnoses too many or too few parameters' '
+test_expect_success 'git notes copy diagnoses too many or too few arguments' '
 	test_must_fail git notes copy 2>error &&
-	test_i18ngrep "too few parameters" error &&
+	test_i18ngrep "too few arguments" error &&
 	test_must_fail git notes copy one two three 2>error &&
-	test_i18ngrep "too many parameters" error
+	test_i18ngrep "too many arguments" error
 '
 
-test_expect_success 'git notes get-ref expands refs/heads/master to refs/notes/refs/heads/master' '
+test_expect_success 'git notes get-ref expands refs/heads/main to refs/notes/refs/heads/main' '
 	test_unconfig core.notesRef &&
 	sane_unset GIT_NOTES_REF &&
-	echo refs/notes/refs/heads/master >expect &&
-	git notes --ref=refs/heads/master get-ref >actual &&
+	echo refs/notes/refs/heads/main >expect &&
+	git notes --ref=refs/heads/main get-ref >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index 7217c5e..ef8b639 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -5,6 +5,9 @@
 
 test_description='Test commit notes index (expensive!)'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 create_repo () {
@@ -20,7 +23,7 @@
 			notemark=$(($mark+1))
 			test_tick &&
 			cat <<-INPUT_END &&
-			commit refs/heads/master
+			commit refs/heads/main
 			mark :$mark
 			committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 			data <<COMMIT
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index 704aee8..d47ce00 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -2,6 +2,9 @@
 
 test_description='Test commit notes organized in subtrees'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 number_of_commits=100
@@ -40,7 +43,7 @@
 			nr=$(($nr+1)) &&
 			test_tick &&
 			cat <<INPUT_END
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 commit #$nr
@@ -75,7 +78,7 @@
 	(
 		start_note_commit &&
 		nr=$number_of_commits &&
-		git rev-list refs/heads/master |
+		git rev-list refs/heads/main |
 		while read sha1; do
 			note_path=$(echo "$sha1" | sed "$1")
 			cat <<INPUT_END &&
@@ -105,7 +108,7 @@
 	(
 		start_note_commit &&
 		nr=$number_of_commits &&
-		git rev-list refs/heads/master |
+		git rev-list refs/heads/main |
 		while read sha1; do
 			first_note_path=$(echo "$sha1" | sed "$1")
 			second_note_path=$(echo "$sha1" | sed "$2")
@@ -144,7 +147,7 @@
 	(
 		start_note_commit &&
 		nr=$number_of_commits &&
-		git rev-list refs/heads/master |
+		git rev-list refs/heads/main |
 		while read sha1; do
 			first_note_path=$(echo "$sha1" | sed "$1")
 			second_note_path=$(echo "$sha1" | sed "$2")
diff --git a/t/t3304-notes-mixed.sh b/t/t3304-notes-mixed.sh
index 1709e8c..03dfcd3 100755
--- a/t/t3304-notes-mixed.sh
+++ b/t/t3304-notes-mixed.sh
@@ -2,6 +2,9 @@
 
 test_description='Test notes trees that also contain non-notes'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 number_of_commits=100
@@ -36,7 +39,7 @@
 
 	test_tick &&
 	cat <<INPUT_END >input &&
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 commit #1
@@ -51,7 +54,7 @@
 
 	test_tick &&
 	cat <<INPUT_END >>input &&
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 commit #2
@@ -68,8 +71,8 @@
 
 test_expect_success "create a notes tree with both notes and non-notes" '
 
-	commit1=$(git rev-parse refs/heads/master^) &&
-	commit2=$(git rev-parse refs/heads/master) &&
+	commit1=$(git rev-parse refs/heads/main^) &&
+	commit2=$(git rev-parse refs/heads/main) &&
 	test_tick &&
 	cat <<INPUT_END >input &&
 commit refs/notes/commits
diff --git a/t/t3308-notes-merge.sh b/t/t3308-notes-merge.sh
index d69c84c..202702b 100755
--- a/t/t3308-notes-merge.sh
+++ b/t/t3308-notes-merge.sh
@@ -108,7 +108,7 @@
 	git update-ref refs/notes/dir/foo HEAD &&
 	test_must_fail git -c "core.notesRef=refs/notes/dir" notes merge x &&
 	test_must_fail git -c "core.notesRef=refs/notes/dir/" notes merge x &&
-	test_must_fail git -c "core.notesRef=refs/heads/master" notes merge x &&
+	test_must_fail git -c "core.notesRef=refs/heads/main" notes merge x &&
 	test_must_fail git -c "core.notesRef=refs/notes/y:" notes merge x &&
 	test_must_fail git -c "core.notesRef=refs/notes/y:foo" notes merge x &&
 	test_must_fail git -c "core.notesRef=refs/notes/foo^{bar" notes merge x
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
index 823fdbd..052516e 100755
--- a/t/t3320-notes-merge-worktrees.sh
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -5,6 +5,9 @@
 
 test_description='Test merging of notes trees in multiple worktrees'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup commit' '
@@ -36,8 +39,8 @@
 '
 
 test_expect_success 'create some new worktrees' '
-	git worktree add -b newbranch worktree master &&
-	git worktree add -b newbranch2 worktree2 master
+	git worktree add -b newbranch worktree main &&
+	git worktree add -b newbranch2 worktree2 main
 '
 
 test_expect_success 'merge z into y fails and sets NOTES_MERGE_REF' '
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 40d2975..587b408 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -8,6 +8,9 @@
 This test runs git rebase and checks that the author information is not lost
 among other things.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 GIT_AUTHOR_NAME=author@name
@@ -24,15 +27,15 @@
 	git update-index --add Y &&
 	git commit -m "Add Y." &&
 	git checkout -b filemove &&
-	git reset --soft master &&
+	git reset --soft main &&
 	mkdir D &&
 	git mv A D/A &&
 	git commit -m "Move A." &&
-	git checkout -b my-topic-branch master &&
+	git checkout -b my-topic-branch main &&
 	echo Second >B &&
 	git update-index --add B &&
 	git commit -m "Add B." &&
-	git checkout -f master &&
+	git checkout -f main &&
 	echo Third >>A &&
 	git update-index A &&
 	git commit -m "Modify A." &&
@@ -46,23 +49,23 @@
 
 test_expect_success 'rebase on dirty worktree' '
 	echo dirty >>A &&
-	test_must_fail git rebase master
+	test_must_fail git rebase main
 '
 
 test_expect_success 'rebase on dirty cache' '
 	git add A &&
-	test_must_fail git rebase master
+	test_must_fail git rebase main
 '
 
-test_expect_success 'rebase against master' '
+test_expect_success 'rebase against main' '
 	git reset --hard HEAD &&
-	git rebase master
+	git rebase main
 '
 
 test_expect_success 'rebase sets ORIG_HEAD to pre-rebase state' '
 	git checkout -b orig-head topic &&
 	pre="$(git rev-parse --verify HEAD)" &&
-	git rebase master &&
+	git rebase main &&
 	test_cmp_rev "$pre" ORIG_HEAD &&
 	test_cmp_rev ! "$pre" HEAD
 '
@@ -93,28 +96,28 @@
 
 test_expect_success 'rebase from ambiguous branch name' '
 	git checkout -b topic side &&
-	git rebase master
+	git rebase main
 '
 
 test_expect_success 'rebase off of the previous branch using "-"' '
-	git checkout master &&
+	git checkout main &&
 	git checkout HEAD^ &&
 	git rebase @{-1} >expect.messages &&
-	git merge-base master HEAD >expect.forkpoint &&
+	git merge-base main HEAD >expect.forkpoint &&
 
-	git checkout master &&
+	git checkout main &&
 	git checkout HEAD^ &&
 	git rebase - >actual.messages &&
-	git merge-base master HEAD >actual.forkpoint &&
+	git merge-base main HEAD >actual.forkpoint &&
 
 	test_cmp expect.forkpoint actual.forkpoint &&
 	# the next one is dubious---we may want to say "-",
 	# instead of @{-1}, in the message
-	test_i18ncmp expect.messages actual.messages
+	test_cmp expect.messages actual.messages
 '
 
 test_expect_success 'rebase a single mode change' '
-	git checkout master &&
+	git checkout main &&
 	git branch -D topic &&
 	echo 1 >X &&
 	git add X &&
@@ -126,7 +129,7 @@
 	test_chmod +x A &&
 	test_tick &&
 	git commit -m modechange &&
-	GIT_TRACE=1 git rebase master
+	GIT_TRACE=1 git rebase main
 '
 
 test_expect_success 'rebase is not broken by diff.renames' '
@@ -162,11 +165,11 @@
 test_expect_success 'rebase works with format.useAutoBase' '
 	test_config format.useAutoBase true &&
 	git checkout topic &&
-	git rebase master
+	git rebase main
 '
 
 test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
-	git checkout -b default-base master &&
+	git checkout -b default-base main &&
 	git checkout -b default topic &&
 	git config branch.default.remote . &&
 	git config branch.default.merge refs/heads/default-base &&
@@ -184,7 +187,7 @@
 '
 
 test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--apply)' '
-	git checkout -B default-base master &&
+	git checkout -B default-base main &&
 	git checkout -B default topic &&
 	git config branch.default.remote . &&
 	git config branch.default.merge refs/heads/default-base &&
@@ -226,13 +229,13 @@
 
 test_expect_success 'rebase --apply -q is quiet' '
 	git checkout -b quiet topic &&
-	git rebase --apply -q master >output.out 2>&1 &&
+	git rebase --apply -q main >output.out 2>&1 &&
 	test_must_be_empty output.out
 '
 
 test_expect_success 'rebase --merge -q is quiet' '
 	git checkout -B quiet topic &&
-	git rebase --merge -q master >output.out 2>&1 &&
+	git rebase --merge -q main >output.out 2>&1 &&
 	test_must_be_empty output.out
 '
 
@@ -294,7 +297,7 @@
 '
 
 test_expect_success 'rebase with "From " line in commit message' '
-	git checkout -b preserve-from master~1 &&
+	git checkout -b preserve-from main~1 &&
 	cat >From_.msg <<EOF &&
 Somebody embedded an mbox in a commit message
 
@@ -310,7 +313,7 @@
 	>From_ &&
 	git add From_ &&
 	git commit -F From_.msg &&
-	git rebase master &&
+	git rebase main &&
 	git log -1 --pretty=format:%B >out &&
 	test_cmp From_.msg out
 '
@@ -357,12 +360,12 @@
 		git cherry-pick test &&
 
 		git checkout test &&
-		git rebase master &&
+		git rebase main &&
 		grep "smudged" a.txt &&
 
 		git checkout removal &&
 		git reset --hard &&
-		git rebase master &&
+		git rebase main &&
 		grep "clean" a.txt
 	)
 '
@@ -402,20 +405,20 @@
 '
 
 test_expect_success 'switch to branch checked out here' '
-	git checkout master &&
-	git rebase master master
+	git checkout main &&
+	git rebase main main
 '
 
 test_expect_success 'switch to branch not checked out' '
-	git checkout master &&
+	git checkout main &&
 	git branch other &&
-	git rebase master other
+	git rebase main other
 '
 
 test_expect_success 'refuse to switch to branch checked out elsewhere' '
-	git checkout master &&
+	git checkout main &&
 	git worktree add wt &&
-	test_must_fail git -C wt rebase master master 2>err &&
+	test_must_fail git -C wt rebase main main 2>err &&
 	test_i18ngrep "already checked out" err
 '
 
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 6e03271..cfde68f 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -5,6 +5,9 @@
 
 test_description='git rebase --merge test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 T="A quick brown fox
@@ -19,10 +22,10 @@
 	git commit -m"initial" &&
 	git branch side &&
 	echo "11 $T" >>original &&
-	git commit -a -m"master updates a bit." &&
+	git commit -a -m"main updates a bit." &&
 
 	echo "12 $T" >>original &&
-	git commit -a -m"master updates a bit more." &&
+	git commit -a -m"main updates a bit more." &&
 
 	git checkout side &&
 	(echo "0 $T" && cat original) >renamed &&
@@ -47,13 +50,13 @@
 '
 
 test_expect_success 'reference merge' '
-	git merge -s recursive -m "reference merge" master
+	git merge -s recursive -m "reference merge" main
 '
 
 PRE_REBASE=$(git rev-parse test-rebase)
 test_expect_success rebase '
 	git checkout test-rebase &&
-	GIT_TRACE=1 git rebase --merge master
+	GIT_TRACE=1 git rebase --merge main
 '
 
 test_expect_success 'test-rebase@{1} is pre rebase' '
@@ -72,24 +75,24 @@
 '
 
 test_expect_success 'rebase the other way' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git rebase --merge side
 '
 
 test_expect_success 'rebase -Xtheirs' '
-	git checkout -b conflicting master~2 &&
+	git checkout -b conflicting main~2 &&
 	echo "AB $T" >> original &&
 	git commit -mconflicting original &&
-	git rebase -Xtheirs master &&
+	git rebase -Xtheirs main &&
 	grep AB original &&
 	! grep 11 original
 '
 
 test_expect_success 'rebase -Xtheirs from orphan' '
-	git checkout --orphan orphan-conflicting master~2 &&
+	git checkout --orphan orphan-conflicting main~2 &&
 	echo "AB $T" >> original &&
 	git commit -morphan-conflicting original &&
-	git rebase -Xtheirs master &&
+	git rebase -Xtheirs main &&
 	grep AB original &&
 	! grep 11 original
 '
@@ -107,9 +110,9 @@
 
 test_expect_success 'picking rebase' '
 	git reset --hard side &&
-	git rebase --merge --onto master side^^ &&
-	mb=$(git merge-base master HEAD) &&
-	if test "$mb" = "$(git rev-parse master)"
+	git rebase --merge --onto main side^^ &&
+	mb=$(git merge-base main HEAD) &&
+	if test "$mb" = "$(git rev-parse main)"
 	then
 		echo happy
 	else
@@ -140,11 +143,11 @@
 	EOF
 	chmod +x test-bin/git-merge-funny &&
 	git reset --hard &&
-	git checkout -b test-funny master^ &&
+	git checkout -b test-funny main^ &&
 	test_commit funny &&
 	(
 		PATH=./test-bin:$PATH &&
-		git rebase -s funny -Xopt master
+		git rebase -s funny -Xopt main
 	) &&
 	test -f funny.was.run
 '
@@ -165,7 +168,7 @@
 test_expect_success '--reapply-cherry-picks' '
 	git init repo &&
 
-	# O(1-10) -- O(1-11) -- O(0-10) master
+	# O(1-10) -- O(1-11) -- O(0-10) main
 	#        \
 	#         -- O(1-11) -- O(1-12) otherbranch
 
@@ -187,18 +190,18 @@
 	git -C repo commit -a -m "add 12 in another branch" &&
 
 	# Regular rebase fails, because the 1-11 commit is deduplicated
-	test_must_fail git -C repo rebase --merge master 2> err &&
+	test_must_fail git -C repo rebase --merge main 2> err &&
 	test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
 	git -C repo rebase --abort &&
 
 	# With --reapply-cherry-picks, it works
-	git -C repo rebase --merge --reapply-cherry-picks master
+	git -C repo rebase --merge --reapply-cherry-picks main
 '
 
 test_expect_success '--reapply-cherry-picks refrains from reading unneeded blobs' '
 	git init server &&
 
-	# O(1-10) -- O(1-11) -- O(1-12) master
+	# O(1-10) -- O(1-11) -- O(1-12) main
 	#        \
 	#         -- O(0-10) otherbranch
 
@@ -220,18 +223,18 @@
 	test_config -C server uploadpack.allowanysha1inwant 1 &&
 
 	git clone --filter=blob:none "file://$(pwd)/server" client &&
-	git -C client checkout origin/master &&
+	git -C client checkout origin/main &&
 	git -C client checkout origin/otherbranch &&
 
 	# Sanity check to ensure that the blobs from the merge base and "add
 	# 11" are missing
 	git -C client rev-list --objects --all --missing=print >missing_list &&
-	MERGE_BASE_BLOB=$(git -C server rev-parse master^^:file.txt) &&
-	ADD_11_BLOB=$(git -C server rev-parse master^:file.txt) &&
+	MERGE_BASE_BLOB=$(git -C server rev-parse main^^:file.txt) &&
+	ADD_11_BLOB=$(git -C server rev-parse main^:file.txt) &&
 	grep "[?]$MERGE_BASE_BLOB" missing_list &&
 	grep "[?]$ADD_11_BLOB" missing_list &&
 
-	git -C client rebase --merge --reapply-cherry-picks origin/master &&
+	git -C client rebase --merge --reapply-cherry-picks origin/main &&
 
 	# The blob from the merge base had to be fetched, but not "add 11"
 	git -C client rev-list --objects --all --missing=print >missing_list &&
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index a927774..e26762d 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -5,6 +5,9 @@
 
 test_description='git rebase --merge --skip tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -48,7 +51,7 @@
 	'
 
 test_expect_success 'rebase with git am -3 (default)' '
-	test_must_fail git rebase master
+	test_must_fail git rebase main
 '
 
 test_expect_success 'rebase --skip can not be used with other options' '
@@ -64,7 +67,7 @@
 	test refs/heads/skip-reference = $(git symbolic-ref HEAD) &&
 	git branch post-rebase &&
 	git reset --hard pre-rebase &&
-	test_must_fail git rebase master &&
+	test_must_fail git rebase main &&
 	echo "hello" > hello &&
 	git add hello &&
 	git rebase --continue &&
@@ -75,7 +78,7 @@
 test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
 
 test_expect_success 'rebase with --merge' '
-	test_must_fail git rebase --merge master
+	test_must_fail git rebase --merge main
 '
 
 test_expect_success 'rebase --skip with --merge' '
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index b06fc36..66bcbbf 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -25,6 +25,9 @@
  where A, B, D and G all touch file1, and one, two, three, four all
  touch file "conflict".
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -83,7 +86,7 @@
 			git rebase -i HEAD^ >output 2>&1
 	) &&
 	tail -n 1 output >actual &&  # Ignore output about changing todo list
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rebase -i with the exec command' '
@@ -98,7 +101,8 @@
 	) &&
 	test_path_is_file touch-one &&
 	test_path_is_file touch-two &&
-	test_path_is_missing touch-three " (should have stopped before)" &&
+	# Missing because we should have stopped by now.
+	test_path_is_missing touch-three &&
 	test_cmp_rev C HEAD &&
 	git rebase --continue &&
 	test_path_is_file touch-three &&
@@ -155,9 +159,9 @@
 	test_when_finished "git rebase --abort ||:" &&
 	test_must_fail env git rebase -x "" @ 2>actual &&
 	test_write_lines "error: empty exec command" >expected &&
-	test_i18ncmp expected actual &&
+	test_cmp expected actual &&
 	test_must_fail env git rebase -x " " @ 2>actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'rebase -x with newline in command fails' '
@@ -165,7 +169,7 @@
 	test_must_fail env git rebase -x "a${LF}b" @ 2>actual &&
 	test_write_lines "error: exec commands cannot contain newlines" \
 			 >expected &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'rebase -i with exec of inexistent command' '
@@ -447,7 +451,7 @@
 	grep "^ file1 | 2 +-$" output
 '
 
-test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
+test_expect_success 'multi-squash only fires up editor once' '
 	base=$(git rev-parse HEAD~4) &&
 	(
 		set_fake_editor &&
@@ -460,7 +464,7 @@
 	test 1 = $(git show | grep ONCE | wc -l)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '
+test_expect_success 'multi-fixup does not fire up editor' '
 	git checkout -b multi-fixup E &&
 	base=$(git rev-parse HEAD~4) &&
 	(
@@ -511,7 +515,7 @@
 	git branch -D conflict-squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
+test_expect_success 'squash and fixup generate correct log messages' '
 	cat >expect-squash-fixup <<-\EOF &&
 	B
 
@@ -538,7 +542,7 @@
 	git branch -D squash-fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
+test_expect_success 'squash ignores comments' '
 	git checkout -b skip-comments E &&
 	base=$(git rev-parse HEAD~4) &&
 	(
@@ -554,7 +558,7 @@
 	git branch -D skip-comments
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '
+test_expect_success 'squash ignores blank lines' '
 	git checkout -b skip-blank-lines E &&
 	base=$(git rev-parse HEAD~4) &&
 	(
@@ -992,7 +996,7 @@
 	test_cmp expected actual
 '
 
-test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '
+test_expect_success 'rebase -ix with --autosquash' '
 	git reset --hard execute &&
 	git checkout -b autosquash &&
 	echo second >second.txt &&
@@ -1133,7 +1137,7 @@
 	test "$(git rev-list --count HEAD)" = 2
 '
 
-test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
+test_expect_success 'rebase --edit-todo does not work on non-interactive rebase' '
 	git checkout reword-original-root-branch &&
 	git reset --hard &&
 	git checkout conflict-branch &&
@@ -1462,7 +1466,7 @@
 		FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual.2
 	) &&
 	head -n4 actual.2 >actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
@@ -1486,7 +1490,7 @@
 		set_fake_editor &&
 		test_must_fail env FAKE_LINES="1 2 4" \
 			git rebase -i --root 2>actual &&
-		test_i18ncmp expect actual &&
+		test_cmp expect actual &&
 		cp .git/rebase-merge/git-rebase-todo.backup \
 			.git/rebase-merge/git-rebase-todo &&
 		FAKE_LINES="1 2 drop 3 4 drop 5" git rebase --edit-todo
@@ -1532,11 +1536,11 @@
 		cp .git/rebase-merge/git-rebase-todo.backup orig &&
 		FAKE_LINES="2 3 4" git rebase --edit-todo 2>actual.2 &&
 		head -n6 actual.2 >actual &&
-		test_i18ncmp expect actual &&
+		test_cmp expect actual &&
 		cp orig .git/rebase-merge/git-rebase-todo &&
 		FAKE_LINES="1 2 3 4" git rebase --edit-todo 2>actual.2 &&
 		head -n4 actual.2 >actual &&
-		test_i18ncmp expect.3 actual &&
+		test_cmp expect.3 actual &&
 		git rebase --continue 2>actual
 	) &&
 	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
@@ -1572,16 +1576,16 @@
 		cp .git/rebase-merge/git-rebase-todo.backup orig &&
 		test_must_fail env FAKE_LINES="2 3 4" \
 			git rebase --edit-todo 2>actual &&
-		test_i18ncmp expect actual &&
+		test_cmp expect actual &&
 		test_must_fail git rebase --continue 2>actual &&
-		test_i18ncmp expect.2 actual &&
+		test_cmp expect.2 actual &&
 		test_must_fail git rebase --edit-todo &&
 		cp orig .git/rebase-merge/git-rebase-todo &&
 		test_must_fail env FAKE_LINES="1 2 3 4" \
 			git rebase --edit-todo 2>actual &&
-		test_i18ncmp expect.3 actual &&
+		test_cmp expect.3 actual &&
 		test_must_fail git rebase --continue 2>actual &&
-		test_i18ncmp expect.3 actual &&
+		test_cmp expect.3 actual &&
 		cp orig .git/rebase-merge/git-rebase-todo &&
 		FAKE_LINES="1 2 3 4 drop 5" git rebase --edit-todo &&
 		git rebase --continue 2>actual
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
index 860e63e..2524331 100755
--- a/t/t3405-rebase-malformed.sh
+++ b/t/t3405-rebase-malformed.sh
@@ -2,6 +2,9 @@
 
 test_description='rebase should handle arbitrary git message'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
@@ -52,7 +55,7 @@
 	git add file3 &&
 	git commit --allow-empty-message -m "" &&
 
-	git checkout master &&
+	git checkout main &&
 
 	echo One >file1 &&
 	test_tick &&
@@ -62,7 +65,7 @@
 
 test_expect_success 'rebase commit with multi-line subject' '
 
-	git rebase master multi-line-subject &&
+	git rebase main multi-line-subject &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
 
 	test_cmp F0 F1 &&
@@ -70,14 +73,14 @@
 '
 
 test_expect_success 'rebase commit with diff in message' '
-	git rebase master diff-in-message &&
+	git rebase main diff-in-message &&
 	git cat-file commit HEAD | sed -e "1,/^$/d" >G1 &&
 	test_cmp G0 G1 &&
 	test_cmp G G0
 '
 
 test_expect_success 'rebase -m commit with empty message' '
-	git rebase -m master empty-message-merge
+	git rebase -m main empty-message-merge
 '
 
 test_expect_success 'rebase -i commit with empty message' '
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 4afc528..77a313f 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -2,6 +2,9 @@
 
 test_description='messages from rebase operation'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -18,27 +21,27 @@
 '
 
 test_expect_success 'rebase -m' '
-	git rebase -m master >actual &&
+	git rebase -m main >actual &&
 	test_must_be_empty actual
 '
 
-test_expect_success 'rebase against master twice' '
-	git rebase --apply master >out &&
+test_expect_success 'rebase against main twice' '
+	git rebase --apply main >out &&
 	test_i18ngrep "Current branch topic is up to date" out
 '
 
-test_expect_success 'rebase against master twice with --force' '
-	git rebase --force-rebase --apply master >out &&
+test_expect_success 'rebase against main twice with --force' '
+	git rebase --force-rebase --apply main >out &&
 	test_i18ngrep "Current branch topic is up to date, rebase forced" out
 '
 
-test_expect_success 'rebase against master twice from another branch' '
+test_expect_success 'rebase against main twice from another branch' '
 	git checkout topic^ &&
-	git rebase --apply master topic >out &&
+	git rebase --apply main topic >out &&
 	test_i18ngrep "Current branch topic is up to date" out
 '
 
-test_expect_success 'rebase fast-forward to master' '
+test_expect_success 'rebase fast-forward to main' '
 	git checkout topic^ &&
 	git rebase --apply topic >out &&
 	test_i18ngrep "Fast-forwarded HEAD to topic" out
@@ -46,31 +49,24 @@
 
 test_expect_success 'rebase --stat' '
 	git reset --hard start &&
-	git rebase --stat master >diffstat.txt &&
+	git rebase --stat main >diffstat.txt &&
 	grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase w/config rebase.stat' '
 	git reset --hard start &&
 	git config rebase.stat true &&
-	git rebase master >diffstat.txt &&
+	git rebase main >diffstat.txt &&
 	grep "^ fileX |  *1 +$" diffstat.txt
 '
 
 test_expect_success 'rebase -n overrides config rebase.stat config' '
 	git reset --hard start &&
 	git config rebase.stat true &&
-	git rebase -n master >diffstat.txt &&
+	git rebase -n main >diffstat.txt &&
 	! grep "^ fileX |  *1 +$" diffstat.txt
 '
 
-# Output to stderr:
-#
-#     "Does not point to a valid commit: invalid-ref"
-#
-# NEEDSWORK: This "grep" is fine in real non-C locales, but
-# GIT_TEST_GETTEXT_POISON poisons the refname along with the enclosing
-# error message.
 test_expect_success 'rebase --onto outputs the invalid ref' '
 	test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err &&
 	test_i18ngrep "invalid-ref" err
@@ -113,7 +109,7 @@
 	git init unrelated &&
 	test_commit -C unrelated 1 &&
 	git -C unrelated remote add -f origin "$PWD" &&
-	git -C unrelated branch --set-upstream-to=origin/master &&
+	git -C unrelated branch --set-upstream-to=origin/main &&
 	git -C unrelated -c core.editor=true rebase -i -v --stat >actual &&
 	test_i18ngrep "Changes to " actual &&
 	test_i18ngrep "5 files changed" actual
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 97efea0..7c381fb 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -2,6 +2,9 @@
 
 test_description='git rebase --abort tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 ### Test that we handle space characters properly
@@ -37,7 +40,7 @@
 		cd "$work_dir" &&
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
-		test_must_fail git rebase$type master &&
+		test_must_fail git rebase$type main &&
 		test_path_is_dir "$dotest" &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
@@ -48,10 +51,10 @@
 		cd "$work_dir" &&
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
-		test_must_fail git rebase$type master &&
+		test_must_fail git rebase$type main &&
 		test_path_is_dir "$dotest" &&
 		test_must_fail git rebase --skip &&
-		test $(git rev-parse HEAD) = $(git rev-parse master) &&
+		test $(git rev-parse HEAD) = $(git rev-parse main) &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
 		test ! -d "$dotest"
@@ -61,13 +64,13 @@
 		cd "$work_dir" &&
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
-		test_must_fail git rebase$type master &&
+		test_must_fail git rebase$type main &&
 		test_path_is_dir "$dotest" &&
 		echo c > a &&
 		echo d >> a &&
 		git add a &&
 		test_must_fail git rebase --continue &&
-		test $(git rev-parse HEAD) != $(git rev-parse master) &&
+		test $(git rev-parse HEAD) != $(git rev-parse main) &&
 		git rebase --abort &&
 		test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
 		test ! -d "$dotest"
@@ -78,7 +81,7 @@
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
 		git reflog show to-rebase > reflog_before &&
-		test_must_fail git rebase$type master &&
+		test_must_fail git rebase$type main &&
 		git rebase --abort &&
 		git reflog show to-rebase > reflog_after &&
 		test_cmp reflog_before reflog_after &&
@@ -89,7 +92,7 @@
 		cd "$work_dir" &&
 		# Clean up the state from the previous one
 		git reset --hard pre-rebase &&
-		test_must_fail git rebase$type master &&
+		test_must_fail git rebase$type main &&
 		test_must_fail git rebase -v --abort &&
 		test_must_fail git rebase --abort -v &&
 		git rebase --abort
@@ -103,7 +106,7 @@
 	cd "$work_dir" &&
 	# Clean up the state from the previous one
 	git reset --hard pre-rebase &&
-	test_must_fail git rebase --apply master &&
+	test_must_fail git rebase --apply main &&
 	test_path_is_dir .git/rebase-apply &&
 	head_before=$(git rev-parse HEAD) &&
 	git rebase --quit &&
@@ -115,7 +118,7 @@
 	cd "$work_dir" &&
 	# Clean up the state from the previous one
 	git reset --hard pre-rebase &&
-	test_must_fail git rebase --merge master &&
+	test_must_fail git rebase --merge main &&
 	test_path_is_dir .git/rebase-merge &&
 	head_before=$(git rev-parse HEAD) &&
 	git rebase --quit &&
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index d2bd7c1..ab0960e 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -2,6 +2,9 @@
 
 test_description='rebasing a commit with multi-line first paragraph.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -46,7 +49,7 @@
 test_expect_success rebase '
 
 	git checkout side &&
-	git rebase master &&
+	git rebase main &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
 	test_cmp expect actual
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index 3b340f1..ec8062a 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -6,6 +6,9 @@
 
 Run "git rebase -p" and check that merges are properly carried along
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq REBASE_P; then
@@ -18,7 +21,7 @@
 
 # Clone 2 (conflicting merge):
 #
-# A1--A2--B3   <-- origin/master
+# A1--A2--B3   <-- origin/main
 #  \       \
 #   B1------M  <-- topic
 #    \
@@ -26,7 +29,7 @@
 #
 # Clone 3 (no-ff merge):
 #
-# A1--A2--B3   <-- origin/master
+# A1--A2--B3   <-- origin/main
 #  \
 #   B1------M  <-- topic
 #    \     /
@@ -44,7 +47,7 @@
 	echo Second > B &&
 	git add B &&
 	git commit -m "Add B1" &&
-	git checkout -f master &&
+	git checkout -f main &&
 	echo Third >> A &&
 	git commit -a -m "Modify A2" &&
 	echo Fifth > B &&
@@ -55,10 +58,10 @@
 	(
 		cd clone2 &&
 		git checkout -b topic origin/topic &&
-		test_must_fail git merge origin/master &&
+		test_must_fail git merge origin/main &&
 		echo Resolved >B &&
 		git add B &&
-		git commit -m "Merge origin/master into topic"
+		git commit -m "Merge origin/main into topic"
 	) &&
 
 	git clone ./. clone3 &&
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 21632a9..fda62c6 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -4,6 +4,9 @@
 
 Tests if git rebase --root --onto <newparent> can rebase the root commit.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 log_with_names () {
@@ -24,7 +27,7 @@
 
 test_expect_success 'rebase --root fails with too many args' '
 	git checkout -B fail other &&
-	test_must_fail git rebase --onto master --root fail fail
+	test_must_fail git rebase --onto main --root fail fail
 '
 
 test_expect_success 'setup pre-rebase hook' '
@@ -44,7 +47,7 @@
 
 test_expect_success 'rebase --root --onto <newbase>' '
 	git checkout -b work other &&
-	git rebase --root --onto master &&
+	git rebase --root --onto main &&
 	git log --pretty=tformat:"%s" > rebased &&
 	test_cmp expect rebased
 '
@@ -55,7 +58,7 @@
 
 test_expect_success 'rebase --root --onto <newbase> <branch>' '
 	git branch work2 other &&
-	git rebase --root --onto master work2 &&
+	git rebase --root --onto main work2 &&
 	git log --pretty=tformat:"%s" > rebased2 &&
 	test_cmp expect rebased2
 '
@@ -66,7 +69,7 @@
 
 test_expect_success 'rebase -i --root --onto <newbase>' '
 	git checkout -b work3 other &&
-	git rebase -i --root --onto master &&
+	git rebase -i --root --onto main &&
 	git log --pretty=tformat:"%s" > rebased3 &&
 	test_cmp expect rebased3
 '
@@ -77,7 +80,7 @@
 
 test_expect_success 'rebase -i --root --onto <newbase> <branch>' '
 	git branch work4 other &&
-	git rebase -i --root --onto master work4 &&
+	git rebase -i --root --onto main work4 &&
 	git log --pretty=tformat:"%s" > rebased4 &&
 	test_cmp expect rebased4
 '
@@ -88,7 +91,7 @@
 
 test_expect_success REBASE_P 'rebase -i -p with linear history' '
 	git checkout -b work5 other &&
-	git rebase -i -p --root --onto master &&
+	git rebase -i -p --root --onto main &&
 	git log --pretty=tformat:"%s" > rebased5 &&
 	test_cmp expect rebased5
 '
@@ -122,7 +125,7 @@
 
 test_expect_success REBASE_P 'rebase -i -p with merge' '
 	git checkout -b work6 other &&
-	git rebase -i -p --root --onto master &&
+	git rebase -i -p --root --onto main &&
 	log_with_names work6 > rebased6 &&
 	test_cmp expect-side rebased6
 '
@@ -157,7 +160,7 @@
 
 test_expect_success REBASE_P 'rebase -i -p with two roots' '
 	git checkout -b work7 other &&
-	git rebase -i -p --root --onto master &&
+	git rebase -i -p --root --onto main &&
 	log_with_names work7 > rebased7 &&
 	test_cmp expect-third rebased7
 '
@@ -173,14 +176,14 @@
 
 test_expect_success 'pre-rebase hook stops rebase' '
 	git checkout -b stops1 other &&
-	test_must_fail git rebase --root --onto master &&
+	test_must_fail git rebase --root --onto main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 &&
 	test 0 = $(git rev-list other...stops1 | wc -l)
 '
 
 test_expect_success 'pre-rebase hook stops rebase -i' '
 	git checkout -b stops2 other &&
-	test_must_fail git rebase --root --onto master &&
+	test_must_fail git rebase --root --onto main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 &&
 	test 0 = $(git rev-list other...stops2 | wc -l)
 '
@@ -190,7 +193,7 @@
 '
 
 test_expect_success 'set up a conflict' '
-	git checkout master &&
+	git checkout main &&
 	echo conflict > B &&
 	git add B &&
 	git commit -m conflict
@@ -198,7 +201,7 @@
 
 test_expect_success 'rebase --root with conflict (first part)' '
 	git checkout -b conflict1 other &&
-	test_must_fail git rebase --root --onto master &&
+	test_must_fail git rebase --root --onto main &&
 	git ls-files -u | grep "B$"
 '
 
@@ -225,7 +228,7 @@
 
 test_expect_success 'rebase -i --root with conflict (first part)' '
 	git checkout -b conflict2 other &&
-	test_must_fail git rebase -i --root --onto master &&
+	test_must_fail git rebase -i --root --onto main &&
 	git ls-files -u | grep "B$"
 '
 
@@ -263,7 +266,7 @@
 
 test_expect_success REBASE_P 'rebase -i -p --root with conflict (first part)' '
 	git checkout -b conflict3 other &&
-	test_must_fail git rebase -i -p --root --onto master &&
+	test_must_fail git rebase -i -p --root --onto main &&
 	git ls-files -u | grep "B$"
 '
 
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
index b6833e9..b4acb3b 100755
--- a/t/t3413-rebase-hook.sh
+++ b/t/t3413-rebase-hook.sh
@@ -2,6 +2,9 @@
 
 test_description='git rebase with its hook(s)'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -18,7 +21,7 @@
 	git add git &&
 	test_tick &&
 	git commit -m side &&
-	git checkout master &&
+	git checkout main &&
 	git log --pretty=oneline --abbrev-commit --graph --all &&
 	git branch test side
 '
@@ -26,14 +29,14 @@
 test_expect_success 'rebase' '
 	git checkout test &&
 	git reset --hard side &&
-	git rebase master &&
+	git rebase main &&
 	test "z$(cat git)" = zworld
 '
 
 test_expect_success 'rebase -i' '
 	git checkout test &&
 	git reset --hard side &&
-	EDITOR=true git rebase -i master &&
+	EDITOR=true git rebase -i main &&
 	test "z$(cat git)" = zworld
 '
 
@@ -49,53 +52,53 @@
 test_expect_success 'pre-rebase hook gets correct input (1)' '
 	git checkout test &&
 	git reset --hard side &&
-	git rebase master &&
+	git rebase main &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,
 
 '
 
 test_expect_success 'pre-rebase hook gets correct input (2)' '
 	git checkout test &&
 	git reset --hard side &&
-	git rebase master test &&
+	git rebase main test &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,test
 '
 
 test_expect_success 'pre-rebase hook gets correct input (3)' '
 	git checkout test &&
 	git reset --hard side &&
-	git checkout master &&
-	git rebase master test &&
+	git checkout main &&
+	git rebase main test &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,test
 '
 
 test_expect_success 'pre-rebase hook gets correct input (4)' '
 	git checkout test &&
 	git reset --hard side &&
-	EDITOR=true git rebase -i master &&
+	EDITOR=true git rebase -i main &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,
 
 '
 
 test_expect_success 'pre-rebase hook gets correct input (5)' '
 	git checkout test &&
 	git reset --hard side &&
-	EDITOR=true git rebase -i master test &&
+	EDITOR=true git rebase -i main test &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,test
 '
 
 test_expect_success 'pre-rebase hook gets correct input (6)' '
 	git checkout test &&
 	git reset --hard side &&
-	git checkout master &&
-	EDITOR=true git rebase -i master test &&
+	git checkout main &&
+	EDITOR=true git rebase -i main test &&
 	test "z$(cat git)" = zworld &&
-	test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+	test "z$(cat .git/PRE-REBASE-INPUT)" = zmain,test
 '
 
 test_expect_success 'setup pre-rebase hook that fails' '
@@ -110,7 +113,7 @@
 test_expect_success 'pre-rebase hook stops rebase (1)' '
 	git checkout test &&
 	git reset --hard side &&
-	test_must_fail git rebase master &&
+	test_must_fail git rebase main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
 	test 0 = $(git rev-list HEAD...side | wc -l)
 '
@@ -118,7 +121,7 @@
 test_expect_success 'pre-rebase hook stops rebase (2)' '
 	git checkout test &&
 	git reset --hard side &&
-	test_must_fail env EDITOR=: git rebase -i master &&
+	test_must_fail env EDITOR=: git rebase -i main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
 	test 0 = $(git rev-list HEAD...side | wc -l)
 '
@@ -126,7 +129,7 @@
 test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
 	git checkout test &&
 	git reset --hard side &&
-	git rebase --no-verify master &&
+	git rebase --no-verify main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
 	test "z$(cat git)" = zworld
 '
@@ -134,7 +137,7 @@
 test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
 	git checkout test &&
 	git reset --hard side &&
-	EDITOR=true git rebase --no-verify -i master &&
+	EDITOR=true git rebase --no-verify -i main &&
 	test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
 	test "z$(cat git)" = zworld
 '
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 7bab600..908016c 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -2,6 +2,9 @@
 
 test_description='auto squash'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -303,23 +306,23 @@
 	fi
 }
 
-test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' '
+test_expect_success 'fixup! fixup!' '
 	test_auto_fixup_fixup fixup fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' '
+test_expect_success 'fixup! squash!' '
 	test_auto_fixup_fixup fixup squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash! squash!' '
+test_expect_success 'squash! squash!' '
 	test_auto_fixup_fixup squash squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' '
+test_expect_success 'squash! fixup!' '
 	test_auto_fixup_fixup squash fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '
+test_expect_success 'autosquash with custom inst format' '
 	git reset --hard base &&
 	git config --add rebase.instructionFormat "[%an @ %ar] %s"  &&
 	echo 2 >file1 &&
@@ -407,7 +410,7 @@
 
 test_expect_success 'abort last squash' '
 	test_when_finished "test_might_fail git rebase --abort" &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 
 	git checkout -b some-squashes &&
 	git commit --allow-empty -m first &&
@@ -440,4 +443,12 @@
 	test XZWY = $(git show | tr -cd W-Z)
 '
 
+test_expect_success 'fixup does not clean up commit message' '
+	oneline="#818" &&
+	git commit --allow-empty -m "$oneline" &&
+	git commit --fixup HEAD --allow-empty &&
+	git -c commit.cleanup=strip rebase -ki --autosquash HEAD~2 &&
+	test "$oneline" = "$(git show -s --format=%s)"
+'
+
 test_done
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
index 9c25484..3716a42 100755
--- a/t/t3416-rebase-onto-threedots.sh
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -2,17 +2,20 @@
 
 test_description='git rebase --onto A...B'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-rebase.sh"
 
-# Rebase only the tip commit of "topic" on merge base between "master"
-# and "topic".  Cannot do this for "side" with "master" because there
+# Rebase only the tip commit of "topic" on merge base between "main"
+# and "topic".  Cannot do this for "side" with "main" because there
 # is no single merge base.
 #
 #
 #	    F---G topic                             G'
 #	   /                                       /
-# A---B---C---D---E master      -->       A---B---C---D---E
+# A---B---C---D---E main        -->       A---B---C---D---E
 #      \   \ /
 #	\   x
 #	 \ / \
@@ -26,7 +29,7 @@
 	git branch topic &&
 	git checkout side &&
 	test_commit H &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git merge H &&
 	git tag D &&
@@ -42,83 +45,83 @@
 	test_commit K
 '
 
-test_expect_success 'rebase --onto master...topic' '
+test_expect_success 'rebase --onto main...topic' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 
-	git rebase --onto master...topic F &&
+	git rebase --onto main...topic F &&
 	git rev-parse HEAD^1 >actual &&
 	git rev-parse C^0 >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --onto master...' '
+test_expect_success 'rebase --onto main...' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 
-	git rebase --onto master... F &&
+	git rebase --onto main... F &&
 	git rev-parse HEAD^1 >actual &&
 	git rev-parse C^0 >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --onto master...side' '
+test_expect_success 'rebase --onto main...side' '
 	git reset --hard &&
 	git checkout side &&
 	git reset --hard K &&
 
-	test_must_fail git rebase --onto master...side J
+	test_must_fail git rebase --onto main...side J
 '
 
-test_expect_success 'rebase -i --onto master...topic' '
+test_expect_success 'rebase -i --onto main...topic' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 	set_fake_editor &&
-	EXPECT_COUNT=1 git rebase -i --onto master...topic F &&
+	EXPECT_COUNT=1 git rebase -i --onto main...topic F &&
 	git rev-parse HEAD^1 >actual &&
 	git rev-parse C^0 >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase -i --onto master...' '
+test_expect_success 'rebase -i --onto main...' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 	set_fake_editor &&
-	EXPECT_COUNT=1 git rebase -i --onto master... F &&
+	EXPECT_COUNT=1 git rebase -i --onto main... F &&
 	git rev-parse HEAD^1 >actual &&
 	git rev-parse C^0 >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase -i --onto master...side' '
+test_expect_success 'rebase -i --onto main...side' '
 	git reset --hard &&
 	git checkout side &&
 	git reset --hard K &&
 
 	set_fake_editor &&
-	test_must_fail git rebase -i --onto master...side J
+	test_must_fail git rebase -i --onto main...side J
 '
 
 test_expect_success 'rebase --keep-base --onto incompatible' '
-	test_must_fail git rebase --keep-base --onto master...
+	test_must_fail git rebase --keep-base --onto main...
 '
 
 test_expect_success 'rebase --keep-base --root incompatible' '
 	test_must_fail git rebase --keep-base --root
 '
 
-test_expect_success 'rebase --keep-base master from topic' '
+test_expect_success 'rebase --keep-base main from topic' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 
-	git rebase --keep-base master &&
+	git rebase --keep-base main &&
 	git rev-parse C >base.expect &&
-	git merge-base master HEAD >base.actual &&
+	git merge-base main HEAD >base.actual &&
 	test_cmp base.expect base.actual &&
 
 	git rev-parse HEAD~2 >actual &&
@@ -126,23 +129,23 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase --keep-base master from side' '
+test_expect_success 'rebase --keep-base main from side' '
 	git reset --hard &&
 	git checkout side &&
 	git reset --hard K &&
 
-	test_must_fail git rebase --keep-base master
+	test_must_fail git rebase --keep-base main
 '
 
-test_expect_success 'rebase -i --keep-base master from topic' '
+test_expect_success 'rebase -i --keep-base main from topic' '
 	git reset --hard &&
 	git checkout topic &&
 	git reset --hard G &&
 
 	set_fake_editor &&
-	EXPECT_COUNT=2 git rebase -i --keep-base master &&
+	EXPECT_COUNT=2 git rebase -i --keep-base main &&
 	git rev-parse C >base.expect &&
-	git merge-base master HEAD >base.actual &&
+	git merge-base main HEAD >base.actual &&
 	test_cmp base.expect base.actual &&
 
 	git rev-parse HEAD~2 >actual &&
@@ -150,13 +153,13 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'rebase -i --keep-base master from side' '
+test_expect_success 'rebase -i --keep-base main from side' '
 	git reset --hard &&
 	git checkout side &&
 	git reset --hard K &&
 
 	set_fake_editor &&
-	test_must_fail git rebase -i --keep-base master
+	test_must_fail git rebase -i --keep-base main
 '
 
 test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 7a2da97..0838f4e 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -2,6 +2,9 @@
 
 test_description='git rebase --continue tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -15,13 +18,13 @@
 	git checkout -b topic HEAD^ &&
 	test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'interactive rebase --continue works with touched file' '
 	rm -fr .git/rebase-* &&
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 	test-tool chmtime =-60 F1 &&
@@ -31,9 +34,9 @@
 test_expect_success 'non-interactive rebase --continue works with touched file' '
 	rm -fr .git/rebase-* &&
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
-	test_must_fail git rebase --onto master master topic &&
+	test_must_fail git rebase --onto main main topic &&
 	echo "Resolved" >F2 &&
 	git add F2 &&
 	test-tool chmtime =-60 F1 &&
@@ -61,7 +64,7 @@
 	chmod +x test-bin/git-merge-funny &&
 	(
 		PATH=./test-bin:$PATH &&
-		test_must_fail git rebase -s funny -Xopt master topic
+		test_must_fail git rebase -s funny -Xopt main topic
 	) &&
 	test -f funny.was.run &&
 	rm funny.was.run &&
@@ -93,7 +96,7 @@
 	chmod +x test-bin/git-merge-funny &&
 	(
 		PATH=./test-bin:$PATH &&
-		test_must_fail git rebase -i -s funny -Xopt -Xfoo master topic
+		test_must_fail git rebase -i -s funny -Xopt -Xfoo main topic
 	) &&
 	test -f funny.was.run &&
 	rm funny.was.run &&
@@ -188,11 +191,11 @@
 test_expect_success 'setup rerere database' '
 	rm -fr .git/rebase-* &&
 	git reset --hard commit-new-file-F3-on-topic-branch &&
-	git checkout master &&
+	git checkout main &&
 	test_commit "commit-new-file-F3" F3 3 &&
 	test_config rerere.enabled true &&
 	git update-ref refs/heads/topic commit-new-file-F3-on-topic-branch &&
-	test_must_fail git rebase -m master topic &&
+	test_must_fail git rebase -m main topic &&
 	echo "Resolved" >F2 &&
 	cp F2 expected-F2 &&
 	git add F2 &&
@@ -207,7 +210,7 @@
 prepare () {
 	rm -fr .git/rebase-* &&
 	git reset --hard commit-new-file-F3-on-topic-branch &&
-	git checkout master &&
+	git checkout main &&
 	test_config rerere.enabled true
 }
 
@@ -215,7 +218,7 @@
 	action=$1 &&
 	test_expect_success "rebase $action --continue remembers --rerere-autoupdate" '
 		prepare &&
-		test_must_fail git rebase $action --rerere-autoupdate master topic &&
+		test_must_fail git rebase $action --rerere-autoupdate main topic &&
 		test_cmp expected-F2 F2 &&
 		git diff-files --quiet &&
 		test_must_fail git rebase --continue &&
@@ -227,7 +230,7 @@
 	test_expect_success "rebase $action --continue honors rerere.autoUpdate" '
 		prepare &&
 		test_config rerere.autoupdate true &&
-		test_must_fail git rebase $action master topic &&
+		test_must_fail git rebase $action main topic &&
 		test_cmp expected-F2 F2 &&
 		git diff-files --quiet &&
 		test_must_fail git rebase --continue &&
@@ -239,7 +242,7 @@
 	test_expect_success "rebase $action --continue remembers --no-rerere-autoupdate" '
 		prepare &&
 		test_config rerere.autoupdate true &&
-		test_must_fail git rebase $action --no-rerere-autoupdate master topic &&
+		test_must_fail git rebase $action --no-rerere-autoupdate main topic &&
 		test_cmp expected-F2 F2 &&
 		test_must_fail git diff-files --quiet &&
 		git add F2 &&
diff --git a/t/t3419-rebase-patch-id.sh b/t/t3419-rebase-patch-id.sh
index 1f32faa..295040f 100755
--- a/t/t3419-rebase-patch-id.sh
+++ b/t/t3419-rebase-patch-id.sh
@@ -2,6 +2,9 @@
 
 test_description='git rebase - test patch id computation'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 scramble () {
@@ -24,7 +27,7 @@
 
 test_expect_success 'setup: 500 lines' '
 	rm -f .gitattributes &&
-	git checkout -q -f master &&
+	git checkout -q -f main &&
 	git reset --hard root &&
 	test_seq 500 >file &&
 	git add file &&
@@ -40,7 +43,7 @@
 	git add newfile &&
 	git commit -q -m "add small file" &&
 
-	git cherry-pick master >/dev/null 2>&1
+	git cherry-pick main >/dev/null 2>&1
 '
 
 test_expect_success 'setup attributes' '
@@ -48,18 +51,18 @@
 '
 
 test_expect_success 'detect upstream patch' '
-	git checkout -q master &&
+	git checkout -q main &&
 	scramble file &&
 	git add file &&
 	git commit -q -m "change big file again" &&
 	git checkout -q other^{} &&
-	git rebase master &&
-	git rev-list master...HEAD~ >revs &&
+	git rebase main &&
+	git rev-list main...HEAD~ >revs &&
 	test_must_be_empty revs
 '
 
 test_expect_success 'do not drop patch' '
-	git branch -f squashed master &&
+	git branch -f squashed main &&
 	git checkout -q -f squashed &&
 	git reset -q --soft HEAD~2 &&
 	git commit -q -m squashed &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index ca33173..43fcb68 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='git rebase --autostash tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -21,12 +24,12 @@
 	git add . &&
 	test_tick &&
 	git commit -m "third commit" &&
-	git checkout -b unrelated-onto-branch master &&
+	git checkout -b unrelated-onto-branch main &&
 	echo unrelated >file4 &&
 	git add . &&
 	test_tick &&
 	git commit -m "unrelated commit" &&
-	git checkout -b related-onto-branch master &&
+	git checkout -b related-onto-branch main &&
 	echo conflicting-change >file2 &&
 	git add . &&
 	test_tick &&
@@ -107,7 +110,7 @@
 		fi &&
 		create_expected_success_$suffix &&
 		sed "$remove_progress_re" <actual >actual2 &&
-		test_i18ncmp expected actual2
+		test_cmp expected actual2
 	'
 
 	test_expect_success "rebase$type: dirty index, non-conflicting rebase" '
@@ -228,7 +231,7 @@
 		fi &&
 		create_expected_failure_$suffix &&
 		sed "$remove_progress_re" <actual >actual2 &&
-		test_i18ncmp expected actual2
+		test_cmp expected actual2
 	'
 }
 
@@ -305,7 +308,7 @@
 '
 
 test_expect_success 'autostash with dirty submodules' '
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	git checkout -b with-submodule &&
 	git submodule add ./ sub &&
 	test_tick &&
@@ -324,7 +327,7 @@
 
 test_expect_success 'never change active branch' '
 	git checkout -b not-the-feature-branch unrelated-onto-branch &&
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	echo changed >file0 &&
 	git rebase --autostash not-the-feature-branch feature-branch &&
 	test_cmp_rev not-the-feature-branch unrelated-onto-branch
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
index 6963750..4859bb8 100755
--- a/t/t3423-rebase-reword.sh
+++ b/t/t3423-rebase-reword.sh
@@ -7,7 +7,7 @@
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
 test_expect_success 'setup' '
-	test_commit master file-1 test &&
+	test_commit main file-1 test &&
 
 	git checkout -b stuff &&
 
@@ -22,7 +22,7 @@
 
 	set_fake_editor &&
 	FAKE_LINES="pick 1 reword 2" FAKE_COMMIT_MESSAGE="feature_b_reworded" \
-		git rebase -i -v master &&
+		git rebase -i -v main &&
 
 	test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
 	test $(git rev-list --count HEAD) = 3
@@ -35,7 +35,7 @@
 
 	set_fake_editor &&
 	test_must_fail env FAKE_LINES="reword 2" \
-		git rebase -i -v master &&
+		git rebase -i -v main &&
 
 	git checkout --theirs file-2 &&
 	git add file-2 &&
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index 5839719..e78c7e3 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -4,6 +4,9 @@
 
 This test runs git rebase and tests the subtree strategy.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
@@ -17,7 +20,7 @@
 #
 # topic_1 - topic_2 - topic_3
 #                             \
-# README ---------------------- Add subproject master - topic_4 - files_subtree/topic_5
+# README ---------------------- Add subproject main - topic_4 - files_subtree/topic_5
 #
 # Where the merge moves the files topic_[123].t into the subdirectory
 # files_subtree/ and topic_4 as well as files_subtree/topic_5 add files to that
@@ -28,7 +31,7 @@
 # an empty commit is added on top. The pre-rebase commit history looks like
 # this:
 #
-# Add subproject master - topic_4 - files_subtree/topic_5 - Empty commit
+# Add subproject main - topic_4 - files_subtree/topic_5 - Empty commit
 #
 # where the root commit adds three files: topic_1.t, topic_2.t and topic_3.t.
 #
@@ -48,11 +51,11 @@
 	test_commit -C files topic_3 &&
 
 	: perform subtree merge into files_subtree/ &&
-	git fetch files refs/heads/master:refs/heads/files-master &&
+	git fetch files refs/heads/main:refs/heads/files-main &&
 	git merge -s ours --no-commit --allow-unrelated-histories \
-		files-master &&
-	git read-tree --prefix=files_subtree -u files-master &&
-	git commit -m "Add subproject master" &&
+		files-main &&
+	git read-tree --prefix=files_subtree -u files-main &&
+	git commit -m "Add subproject main" &&
 
 	: add two extra commits to rebase &&
 	test_commit -C files_subtree topic_4 &&
@@ -70,7 +73,7 @@
 test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' '
 	reset_rebase &&
 	git checkout -b rebase-preserve-merges to-rebase &&
-	git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
+	git rebase -Xsubtree=files_subtree --preserve-merges --onto files-main main &&
 	verbose test "$(commit_message HEAD~)" = "topic_4" &&
 	verbose test "$(commit_message HEAD)" = "files_subtree/topic_5"
 '
@@ -79,7 +82,7 @@
 test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' '
 	reset_rebase &&
 	git checkout -b rebase-keep-empty to-rebase &&
-	git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
+	git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-main main &&
 	verbose test "$(commit_message HEAD~2)" = "topic_4" &&
 	verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
 	verbose test "$(commit_message HEAD)" = "Empty commit"
@@ -88,7 +91,7 @@
 test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
 	reset_rebase &&
 	git checkout -b rebase-onto to-rebase &&
-	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
+	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-main main &&
 	: first pick results in no changes &&
 	git rebase --skip &&
 	verbose test "$(commit_message HEAD~2)" = "topic_4" &&
@@ -99,7 +102,7 @@
 test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
 	reset_rebase &&
 	git checkout -b rebase-merges-onto to-rebase &&
-	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
+	test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-main --root &&
 	: first pick results in no changes &&
 	git rebase --skip &&
 	verbose test "$(commit_message HEAD~2)" = "topic_4" &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index b454f40..6748070 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -12,12 +12,15 @@
 
     -- B --                   (first)
    /       \
- A - C - D - E - H            (master)
+ A - C - D - E - H            (main)
    \    \       /
     \    F - G                (second)
      \
       Conflicting-G
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
@@ -37,7 +40,7 @@
 	git checkout -b first &&
 	test_commit B &&
 	b=$(git rev-parse --short HEAD) &&
-	git checkout master &&
+	git checkout main &&
 	test_commit C &&
 	c=$(git rev-parse --short HEAD) &&
 	test_commit D &&
@@ -52,7 +55,7 @@
 	f=$(git rev-parse --short HEAD) &&
 	test_commit G &&
 	g=$(git rev-parse --short HEAD) &&
-	git checkout master &&
+	git checkout main &&
 	git merge --no-commit G &&
 	test_tick &&
 	git commit -m H &&
@@ -82,7 +85,7 @@
 	EOF
 	test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
 	test_tick &&
-	git rebase -i -r A master &&
+	git rebase -i -r A main &&
 	test_cmp_graph <<-\EOF
 	*   Merge the topic branch '\''onebranch'\''
 	|\
@@ -183,7 +186,7 @@
 '
 
 test_expect_success 'with a branch tip that was cherry-picked already' '
-	git checkout -b already-upstream master &&
+	git checkout -b already-upstream main &&
 	base="$(git rev-parse --verify HEAD)" &&
 
 	test_commit A1 &&
@@ -211,7 +214,7 @@
 '
 
 test_expect_success 'do not rebase cousins unless asked for' '
-	git checkout -b cousins master &&
+	git checkout -b cousins main &&
 	before="$(git rev-parse --verify HEAD)" &&
 	test_tick &&
 	git rebase -r HEAD^ &&
@@ -340,7 +343,7 @@
 test_expect_success 'A root commit can be a cousin, treat it that way' '
 	git checkout --orphan khnum &&
 	test_commit yama &&
-	git checkout -b asherah master &&
+	git checkout -b asherah main &&
 	test_commit shamkat &&
 	git merge --allow-unrelated-histories khnum &&
 	test_tick &&
@@ -367,7 +370,7 @@
 	git checkout -b third B &&
 	test_commit I &&
 	third=$(git rev-parse HEAD) &&
-	git checkout -b labels master &&
+	git checkout -b labels main &&
 	git merge --no-commit third &&
 	test_tick &&
 	git commit -m "Merge commit '\''$third'\'' into labels" &&
diff --git a/t/t3431-rebase-fork-point.sh b/t/t3431-rebase-fork-point.sh
index 1725627..4c98d99 100755
--- a/t/t3431-rebase-fork-point.sh
+++ b/t/t3431-rebase-fork-point.sh
@@ -5,13 +5,16 @@
 
 test_description='git rebase --fork-point test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
-# A---B---D---E    (master)
+# A---B---D---E    (main)
 #      \
 #       C*---F---G (side)
 #
-# C was formerly part of master but master was rewound to remove C
+# C was formerly part of main but main was rewound to remove C
 #
 test_expect_success setup '
 	test_commit A &&
@@ -26,19 +29,23 @@
 	test_commit G
 '
 
+do_test_rebase () {
+	expected="$1" &&
+	shift &&
+	git checkout main &&
+	git reset --hard E &&
+	git checkout side &&
+	git reset --hard G &&
+	git rebase $* &&
+	test_write_lines $expected >expect &&
+	git log --pretty=%s >actual &&
+	test_cmp expect actual
+}
+
 test_rebase () {
 	expected="$1" &&
 	shift &&
-	test_expect_success "git rebase $*" "
-		git checkout master &&
-		git reset --hard E &&
-		git checkout side &&
-		git reset --hard G &&
-		git rebase $* &&
-		test_write_lines $expected >expect &&
-		git log --pretty=%s >actual &&
-		test_cmp expect actual
-	"
+	test_expect_success "git rebase $*" "do_test_rebase '$expected' $*"
 }
 
 test_rebase 'G F E D B A'
@@ -48,30 +55,61 @@
 test_rebase 'G F C D B A' --no-fork-point --onto D
 test_rebase 'G F C B A' --no-fork-point --keep-base
 
-test_rebase 'G F E D B A' --fork-point refs/heads/master
-test_rebase 'G F E D B A' --fork-point master
+test_rebase 'G F E D B A' --fork-point refs/heads/main
+test_rebase 'G F E D B A' --fork-point main
 
-test_rebase 'G F D B A' --fork-point --onto D refs/heads/master
-test_rebase 'G F D B A' --fork-point --onto D master
+test_rebase 'G F D B A' --fork-point --onto D refs/heads/main
+test_rebase 'G F D B A' --fork-point --onto D main
 
-test_rebase 'G F B A' --fork-point --keep-base refs/heads/master
-test_rebase 'G F B A' --fork-point --keep-base master
+test_rebase 'G F B A' --fork-point --keep-base refs/heads/main
+test_rebase 'G F B A' --fork-point --keep-base main
 
-test_rebase 'G F C E D B A' refs/heads/master
-test_rebase 'G F C E D B A' master
+test_rebase 'G F C E D B A' refs/heads/main
+test_rebase 'G F C E D B A' main
 
-test_rebase 'G F C D B A' --onto D refs/heads/master
-test_rebase 'G F C D B A' --onto D master
+test_rebase 'G F C D B A' --onto D refs/heads/main
+test_rebase 'G F C D B A' --onto D main
 
-test_rebase 'G F C B A' --keep-base refs/heads/master
-test_rebase 'G F C B A' --keep-base master
+test_rebase 'G F C B A' --keep-base refs/heads/main
+test_rebase 'G F C B A' --keep-base main
 
 test_expect_success 'git rebase --fork-point with ambigous refname' '
-	git checkout master &&
+	git checkout main &&
 	git checkout -b one &&
 	git checkout side &&
 	git tag one &&
 	test_must_fail git rebase --fork-point --onto D one
 '
 
+test_expect_success '--fork-point and --root both given' '
+	test_must_fail git rebase --fork-point --root 2>err &&
+	test_i18ngrep "cannot combine" err
+'
+
+test_expect_success 'rebase.forkPoint set to false' '
+	test_config rebase.forkPoint false &&
+	do_test_rebase "G F C E D B A"
+'
+
+test_expect_success 'rebase.forkPoint set to false and then to true' '
+	test_config_global rebase.forkPoint false &&
+	test_config rebase.forkPoint true &&
+	do_test_rebase "G F E D B A"
+'
+
+test_expect_success 'rebase.forkPoint set to false and command line says --fork-point' '
+	test_config rebase.forkPoint false &&
+	do_test_rebase "G F E D B A" --fork-point
+'
+
+test_expect_success 'rebase.forkPoint set to true and command line says --no-fork-point' '
+	test_config rebase.forkPoint true &&
+	do_test_rebase "G F C E D B A" --no-fork-point
+'
+
+test_expect_success 'rebase.forkPoint set to true and --root given' '
+	test_config rebase.forkPoint true &&
+	git rebase --root
+'
+
 test_done
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index a29eda8..5086e14 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -5,6 +5,9 @@
 
 test_description='ensure rebase fast-forwards commits when possible'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -84,19 +87,19 @@
 
 changes='no changes'
 test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work same main
 test_rebase_same_head success noop same success work diff --onto B B
 test_rebase_same_head success noop same success work diff --onto B... B
-test_rebase_same_head success noop same success work same --onto master... master
-test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --onto main... main
+test_rebase_same_head success noop same success work same --keep-base main
 test_rebase_same_head success noop same success work same --keep-base
 test_rebase_same_head success noop same success work same --no-fork-point
 test_rebase_same_head success noop same success work same --keep-base --no-fork-point
-test_rebase_same_head success noop same success work same --fork-point master
+test_rebase_same_head success noop same success work same --fork-point main
 test_rebase_same_head success noop same success work diff --fork-point --onto B B
 test_rebase_same_head success noop same success work diff --fork-point --onto B... B
-test_rebase_same_head success noop same success work same --fork-point --onto master... master
-test_rebase_same_head success noop same success work same --keep-base --keep-base master
+test_rebase_same_head success noop same success work same --fork-point --onto main... main
+test_rebase_same_head success noop same success work same --keep-base --keep-base main
 
 test_expect_success 'add work same to side' '
 	test_commit E
@@ -104,22 +107,22 @@
 
 changes='our changes'
 test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work same main
 test_rebase_same_head success noop same success work diff --onto B B
 test_rebase_same_head success noop same success work diff --onto B... B
-test_rebase_same_head success noop same success work same --onto master... master
-test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --onto main... main
+test_rebase_same_head success noop same success work same --keep-base main
 test_rebase_same_head success noop same success work same --keep-base
 test_rebase_same_head success noop same success work same --no-fork-point
 test_rebase_same_head success noop same success work same --keep-base --no-fork-point
-test_rebase_same_head success noop same success work same --fork-point master
+test_rebase_same_head success noop same success work same --fork-point main
 test_rebase_same_head success noop same success work diff --fork-point --onto B B
 test_rebase_same_head success noop same success work diff --fork-point --onto B... B
-test_rebase_same_head success noop same success work same --fork-point --onto master... master
-test_rebase_same_head success noop same success work same --fork-point --keep-base master
+test_rebase_same_head success noop same success work same --fork-point --onto main... main
+test_rebase_same_head success noop same success work same --fork-point --keep-base main
 
 test_expect_success 'add work same to upstream' '
-	git checkout master &&
+	git checkout main &&
 	test_commit F &&
 	git checkout side
 '
@@ -127,12 +130,12 @@
 changes='our and their changes'
 test_rebase_same_head success noop same success work diff --onto B B
 test_rebase_same_head success noop same success work diff --onto B... B
-test_rebase_same_head success noop same success work diff --onto master... master
-test_rebase_same_head success noop same success work diff --keep-base master
+test_rebase_same_head success noop same success work diff --onto main... main
+test_rebase_same_head success noop same success work diff --keep-base main
 test_rebase_same_head success noop same success work diff --keep-base
 test_rebase_same_head failure work same success work diff --fork-point --onto B B
 test_rebase_same_head failure work same success work diff --fork-point --onto B... B
-test_rebase_same_head success noop same success work diff --fork-point --onto master... master
-test_rebase_same_head success noop same success work diff --fork-point --keep-base master
+test_rebase_same_head success noop same success work diff --fork-point --onto main... main
+test_rebase_same_head success noop same success work diff --fork-point --keep-base main
 
 test_done
diff --git a/t/t3434-rebase-i18n.sh b/t/t3434-rebase-i18n.sh
index c7c835c..e6fef69 100755
--- a/t/t3434-rebase-i18n.sh
+++ b/t/t3434-rebase-i18n.sh
@@ -7,13 +7,16 @@
 
 Initial setup:
 
-1 - 2              master
+1 - 2              main
  \
   3 - 4            first
    \
     5 - 6          second
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 compare_msg () {
@@ -41,7 +44,7 @@
 	git config i18n.commitencoding eucJP &&
 	git merge -F "$TEST_DIRECTORY/t3434/eucJP.txt" second &&
 	git config i18n.commitencoding UTF-8 &&
-	git rebase --rebase-merges master &&
+	git rebase --rebase-merges main &&
 	compare_msg eucJP.txt eucJP UTF-8
 '
 
@@ -50,7 +53,7 @@
 	git config i18n.commitencoding eucJP &&
 	git merge -F "$TEST_DIRECTORY/t3434/eucJP.txt" second &&
 	git config i18n.commitencoding ISO-2022-JP &&
-	git rebase --rebase-merges master &&
+	git rebase --rebase-merges main &&
 	compare_msg eucJP.txt eucJP ISO-2022-JP
 '
 
@@ -66,7 +69,7 @@
 		git config i18n.commitencoding $old &&
 		git commit -F "$TEST_DIRECTORY/t3434/$msgfile" &&
 		git config i18n.commitencoding $new &&
-		test_must_fail git rebase -m master &&
+		test_must_fail git rebase -m main &&
 		test -f .git/rebase-merge/message &&
 		git stripspace <.git/rebase-merge/message >two.t &&
 		git add two.t &&
diff --git a/t/t3435-rebase-gpg-sign.sh b/t/t3435-rebase-gpg-sign.sh
index 54120b0..ec10766 100755
--- a/t/t3435-rebase-gpg-sign.sh
+++ b/t/t3435-rebase-gpg-sign.sh
@@ -5,6 +5,9 @@
 
 test_description='test rebase --[no-]gpg-sign'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-rebase.sh"
 . "$TEST_DIRECTORY/lib-gpg.sh"
@@ -46,7 +49,7 @@
 	test_commit fork-point &&
 	git switch -c side &&
 	test_commit three &&
-	git switch master &&
+	git switch main &&
 	git merge --no-ff side &&
 	git tag merged
 '
@@ -64,7 +67,7 @@
 test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
 	git reset --hard merged &&
 	git config commit.gpgsign true &&
-	git rebase -p --no-gpg-sign --onto=one fork-point master &&
+	git rebase -p --no-gpg-sign --onto=one fork-point main &&
 	test_must_fail git verify-commit HEAD
 '
 
diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh
index eaaf4c8..4d10664 100755
--- a/t/t3436-rebase-more-options.sh
+++ b/t/t3436-rebase-more-options.sh
@@ -28,7 +28,7 @@
 	test_commit commit2 foo foo2 &&
 	test_commit commit3 foo foo3 &&
 
-	git checkout --orphan master &&
+	git checkout --orphan main &&
 	rm foo &&
 	test_write_lines "line 1" "        line 2" "line 3" >file &&
 	git commit -am "add file" &&
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
index 2b8d9cb..0458a58 100755
--- a/t/t3500-cherry.sh
+++ b/t/t3500-cherry.sh
@@ -5,9 +5,12 @@
 
 test_description='git cherry should detect patches integrated upstream
 
-This test cherry-picks one local change of two into master branch, and
+This test cherry-picks one local change of two into main branch, and
 checks that git cherry only returns the second patch in the local branch
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 GIT_AUTHOR_EMAIL=bogus_email_address
@@ -32,7 +35,7 @@
      test_tick &&
      git commit -m "Add C." &&
 
-     git checkout -f master &&
+     git checkout -f main &&
      rm -f B C &&
 
      echo Third >> A &&
@@ -40,19 +43,19 @@
      test_tick &&
      git commit -m "Modify A." &&
 
-     expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* + .*"
+     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* + .*"
 '
 
 test_expect_success \
     'check that cherry with limit returns only the top patch'\
-    'expr "$(echo $(git cherry master my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+    'expr "$(echo $(git cherry main my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
 '
 
 test_expect_success \
     'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
     'git cherry-pick my-topic-branch^0 &&
-     echo $(git cherry master my-topic-branch) &&
-     expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* - .*"
+     echo $(git cherry main my-topic-branch) &&
+     expr "$(echo $(git cherry main my-topic-branch) )" : "+ [^ ]* - .*"
 '
 
 test_expect_success 'cherry ignores whitespace' '
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 3669dfb..9d100cd 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -10,6 +10,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -112,7 +115,7 @@
 test_expect_success 'cherry-pick "-" to pick from previous branch' '
 	git checkout unborn &&
 	test_commit to-pick actual content &&
-	git checkout master &&
+	git checkout main &&
 	git cherry-pick - &&
 	echo content >expect &&
 	test_cmp expect actual
@@ -132,7 +135,7 @@
 test_expect_success 'cherry-pick "-" works with arguments' '
 	git checkout -b side-branch &&
 	test_commit change actual change &&
-	git checkout master &&
+	git checkout main &&
 	git cherry-pick -s - &&
 	echo "Signed-off-by: C O Mitter <committer@example.com>" >expect &&
 	git cat-file commit HEAD | grep ^Signed-off-by: >signoff &&
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
index 8b635a1..5495eac 100755
--- a/t/t3502-cherry-pick-merge.sh
+++ b/t/t3502-cherry-pick-merge.sh
@@ -8,6 +8,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -25,7 +28,7 @@
 	echo new line >B &&
 	git commit -m "add line to B" B &&
 	git tag b &&
-	git checkout master &&
+	git checkout main &&
 	git merge side &&
 	git tag c
 
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
index e27f39d..95fe4fe 100755
--- a/t/t3503-cherry-pick-root.sh
+++ b/t/t3503-cherry-pick-root.sh
@@ -2,6 +2,9 @@
 
 test_description='test cherry-picking (and reverting) a root commit'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -30,7 +33,7 @@
 test_expect_success 'cherry-pick a root commit' '
 
 	git checkout second^0 &&
-	git cherry-pick master &&
+	git cherry-pick main &&
 	echo first >expect &&
 	test_cmp expect file1
 
@@ -38,14 +41,14 @@
 
 test_expect_success 'revert a root commit' '
 
-	git revert master &&
+	git revert main &&
 	test_path_is_missing file1
 
 '
 
 test_expect_success 'cherry-pick a root commit with an external strategy' '
 
-	git cherry-pick --strategy=resolve master &&
+	git cherry-pick --strategy=resolve main &&
 	echo first >expect &&
 	test_cmp expect file1
 
@@ -53,7 +56,7 @@
 
 test_expect_success 'revert a root commit with an external strategy' '
 
-	git revert --strategy=resolve master &&
+	git revert --strategy=resolve main &&
 	test_path_is_missing file1
 
 '
@@ -65,7 +68,7 @@
 	echo third >expect.file3 &&
 
 	git checkout second^0 &&
-	git cherry-pick master third &&
+	git cherry-pick main third &&
 
 	test_cmp expect.file1 file1 &&
 	test_cmp expect.file2 file2 &&
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
index 80a0d08..4581ae9 100755
--- a/t/t3504-cherry-pick-rerere.sh
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -2,12 +2,15 @@
 
 test_description='cherry-pick should rerere for conflicts'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
 	test_commit foo &&
-	test_commit foo-master foo &&
-	test_commit bar-master bar &&
+	test_commit foo-main foo &&
+	test_commit bar-main bar &&
 
 	git checkout -b dev foo &&
 	test_commit foo-dev foo &&
@@ -16,7 +19,7 @@
 '
 
 test_expect_success 'conflicting merge' '
-	test_must_fail git merge master
+	test_must_fail git merge main
 '
 
 test_expect_success 'fixup' '
@@ -29,7 +32,7 @@
 '
 
 test_expect_success 'cherry-pick conflict with --rerere-autoupdate' '
-	test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --rerere-autoupdate foo..bar-main &&
 	test_cmp foo-expect foo &&
 	git diff-files --quiet &&
 	test_must_fail git cherry-pick --continue &&
@@ -41,7 +44,7 @@
 
 test_expect_success 'cherry-pick conflict repsects rerere.autoUpdate' '
 	test_config rerere.autoUpdate true &&
-	test_must_fail git cherry-pick foo..bar-master &&
+	test_must_fail git cherry-pick foo..bar-main &&
 	test_cmp foo-expect foo &&
 	git diff-files --quiet &&
 	test_must_fail git cherry-pick --continue &&
@@ -53,7 +56,7 @@
 
 test_expect_success 'cherry-pick conflict with --no-rerere-autoupdate' '
 	test_config rerere.autoUpdate true &&
-	test_must_fail git cherry-pick --no-rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --no-rerere-autoupdate foo..bar-main &&
 	test_cmp foo-expect foo &&
 	test_must_fail git diff-files --quiet &&
 	git add foo &&
@@ -66,38 +69,38 @@
 '
 
 test_expect_success 'cherry-pick --continue rejects --rerere-autoupdate' '
-	test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --rerere-autoupdate foo..bar-main &&
 	test_cmp foo-expect foo &&
 	git diff-files --quiet &&
 	test_must_fail git cherry-pick --continue --rerere-autoupdate >actual 2>&1 &&
 	echo "fatal: cherry-pick: --rerere-autoupdate cannot be used with --continue" >expect &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	test_must_fail git cherry-pick --continue --no-rerere-autoupdate >actual 2>&1 &&
 	echo "fatal: cherry-pick: --no-rerere-autoupdate cannot be used with --continue" >expect &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	git cherry-pick --abort
 '
 
 test_expect_success 'cherry-pick --rerere-autoupdate more than once' '
-	test_must_fail git cherry-pick --rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --rerere-autoupdate --rerere-autoupdate foo..bar-main &&
 	test_cmp foo-expect foo &&
 	git diff-files --quiet &&
 	git cherry-pick --abort &&
-	test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate foo..bar-main &&
 	test_cmp foo-expect foo &&
 	git diff-files --quiet &&
 	git cherry-pick --abort &&
-	test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate foo..bar-master &&
+	test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate foo..bar-main &&
 	test_must_fail git diff-files --quiet &&
 	git cherry-pick --abort
 '
 
 test_expect_success 'cherry-pick conflict without rerere' '
 	test_config rerere.enabled false &&
-	test_must_fail git cherry-pick foo-master &&
+	test_must_fail git cherry-pick foo-main &&
 	grep ===== foo &&
 	grep foo-dev foo &&
-	grep foo-master foo
+	grep foo-main foo
 '
 
 test_done
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
index 5f911bb..eba3c38 100755
--- a/t/t3505-cherry-pick-empty.sh
+++ b/t/t3505-cherry-pick-empty.sh
@@ -2,6 +2,9 @@
 
 test_description='test cherry-picking an empty commit'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -17,7 +20,7 @@
 	test_tick &&
 	git commit --allow-empty-message -m "" &&
 
-	git checkout master &&
+	git checkout main &&
 	git checkout -b empty-change-branch &&
 	test_tick &&
 	git commit --allow-empty -m "empty"
@@ -25,7 +28,7 @@
 '
 
 test_expect_success 'cherry-pick an empty commit' '
-	git checkout master &&
+	git checkout main &&
 	test_expect_code 1 git cherry-pick empty-change-branch
 '
 
@@ -35,7 +38,7 @@
 
 test_expect_success 'cherry-pick a commit with an empty message' '
 	test_when_finished "git reset --hard empty-message-branch~1" &&
-	git checkout master &&
+	git checkout main &&
 	git cherry-pick empty-message-branch
 '
 
@@ -44,12 +47,12 @@
 '
 
 test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git cherry-pick --allow-empty-message empty-message-branch
 '
 
 test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
-	git checkout master &&
+	git checkout main &&
 	echo fourth >>file2 &&
 	git add file2 &&
 	git commit -m "fourth" &&
@@ -57,22 +60,22 @@
 '
 
 test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
-	git checkout master &&
+	git checkout main &&
 	git cherry-pick --allow-empty empty-change-branch
 '
 
 test_expect_success 'cherry pick with --keep-redundant-commits' '
-	git checkout master &&
+	git checkout main &&
 	git cherry-pick --keep-redundant-commits HEAD^
 '
 
 test_expect_success 'cherry-pick a commit that becomes no-op (prep)' '
-	git checkout master &&
+	git checkout main &&
 	git branch fork &&
 	echo foo >file2 &&
 	git add file2 &&
 	test_tick &&
-	git commit -m "add file2 on master" &&
+	git commit -m "add file2 on main" &&
 
 	git checkout fork &&
 	echo foo >file2 &&
@@ -84,15 +87,15 @@
 test_expect_success 'cherry-pick a no-op without --keep-redundant' '
 	git reset --hard &&
 	git checkout fork^0 &&
-	test_must_fail git cherry-pick master
+	test_must_fail git cherry-pick main
 '
 
 test_expect_success 'cherry-pick a no-op with --keep-redundant' '
 	git reset --hard &&
 	git checkout fork^0 &&
-	git cherry-pick --keep-redundant-commits master &&
+	git cherry-pick --keep-redundant-commits main &&
 	git show -s --format=%s >actual &&
-	echo "add file2 on master" >expect &&
+	echo "add file2 on main" >expect &&
 	test_cmp expect actual
 '
 
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
index 9d5adbc..7e11bd4 100755
--- a/t/t3506-cherry-pick-ff.sh
+++ b/t/t3506-cherry-pick-ff.sh
@@ -2,6 +2,9 @@
 
 test_description='test cherry-picking with --ff option'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -24,7 +27,7 @@
 '
 
 test_expect_success 'cherry-pick using --ff fast forwards' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick --ff second &&
@@ -32,7 +35,7 @@
 '
 
 test_expect_success 'cherry-pick not using --ff does not fast forwards' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick second &&
@@ -49,7 +52,7 @@
 # (This has been taken from t3502-cherry-pick-merge.sh)
 #
 test_expect_success 'merge setup' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard first &&
 	echo new line >A &&
 	git add A &&
@@ -62,7 +65,7 @@
 	test_tick &&
 	git commit -m "add line to B" B &&
 	git tag B &&
-	git checkout master &&
+	git checkout main &&
 	git merge side &&
 	git tag C &&
 	git checkout -b new A
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index a21adcf..014001b 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -9,6 +9,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 pristine_detach () {
@@ -29,7 +32,7 @@
 	test_commit redundant-pick foo c redundant &&
 	git commit --allow-empty --allow-empty-message &&
 	git tag empty &&
-	git checkout master &&
+	git checkout main &&
 	git config advice.detachedhead false
 
 '
@@ -56,7 +59,7 @@
 	EOF
 	test_must_fail git cherry-pick picked 2>actual &&
 
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 test_expect_success 'advice from failed cherry-pick --no-commit' "
@@ -70,7 +73,7 @@
 	EOF
 	test_must_fail git cherry-pick --no-commit picked 2>actual &&
 
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
@@ -253,7 +256,7 @@
 
 	test_must_fail git cherry-pick picked &&
 
-	test_i18ncmp expected .git/MERGE_MSG
+	test_cmp expected .git/MERGE_MSG
 '
 
 test_expect_success \
@@ -273,7 +276,7 @@
 
 	test_must_fail git cherry-pick --cleanup=scissors picked &&
 
-	test_i18ncmp expected .git/MERGE_MSG
+	test_cmp expected .git/MERGE_MSG
 '
 
 test_expect_success 'failed cherry-pick describes conflict in work tree' '
@@ -462,7 +465,7 @@
 	test_must_fail git revert picked &&
 
 	sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success \
@@ -485,7 +488,7 @@
 	test_must_fail git revert --cleanup=scissors picked &&
 
 	sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'failed cherry-pick does not forget -s' '
diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
index 23070a7..e8375d1 100755
--- a/t/t3508-cherry-pick-many-commits.sh
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -2,6 +2,9 @@
 
 test_description='test cherry-picking many commits'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_head_differs_from() {
@@ -31,7 +34,7 @@
 '
 
 test_expect_success 'cherry-pick first..fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick first..fourth &&
@@ -45,7 +48,7 @@
 	test_commit one &&
 	test_commit two &&
 	test_commit three &&
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	git cherry-pick three one two &&
 	git diff --quiet three &&
@@ -56,38 +59,38 @@
 '
 
 test_expect_success 'cherry-pick three one two: fails' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_must_fail git cherry-pick three one two:
 '
 
 test_expect_success 'output to keep user entertained during multi-pick' '
 	cat <<-\EOF >expected &&
-	[master OBJID] second
+	[main OBJID] second
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:14:13 2005 -0700
 	 1 file changed, 1 insertion(+)
-	[master OBJID] third
+	[main OBJID] third
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:15:13 2005 -0700
 	 1 file changed, 1 insertion(+)
-	[master OBJID] fourth
+	[main OBJID] fourth
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:16:13 2005 -0700
 	 1 file changed, 1 insertion(+)
 	EOF
 
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick first..fourth >actual &&
 	sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
 	test_line_count -ge 3 actual.fuzzy &&
-	test_i18ncmp expected actual.fuzzy
+	test_cmp expected actual.fuzzy
 '
 
 test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick --strategy resolve first..fourth &&
@@ -99,32 +102,32 @@
 test_expect_success 'output during multi-pick indicates merge strategy' '
 	cat <<-\EOF >expected &&
 	Trying simple merge.
-	[master OBJID] second
+	[main OBJID] second
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:14:13 2005 -0700
 	 1 file changed, 1 insertion(+)
 	Trying simple merge.
-	[master OBJID] third
+	[main OBJID] third
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:15:13 2005 -0700
 	 1 file changed, 1 insertion(+)
 	Trying simple merge.
-	[master OBJID] fourth
+	[main OBJID] fourth
 	 Author: A U Thor <author@example.com>
 	 Date: Thu Apr 7 15:16:13 2005 -0700
 	 1 file changed, 1 insertion(+)
 	EOF
 
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick --strategy resolve first..fourth >actual &&
 	sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
-	test_i18ncmp expected actual.fuzzy
+	test_cmp expected actual.fuzzy
 '
 
 test_expect_success 'cherry-pick --ff first..fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick --ff first..fourth &&
@@ -134,7 +137,7 @@
 '
 
 test_expect_success 'cherry-pick -n first..fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick -n first..fourth &&
@@ -144,7 +147,7 @@
 '
 
 test_expect_success 'revert first..fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard fourth &&
 	test_tick &&
 	git revert first..fourth &&
@@ -154,7 +157,7 @@
 '
 
 test_expect_success 'revert ^first fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard fourth &&
 	test_tick &&
 	git revert ^first fourth &&
@@ -164,7 +167,7 @@
 '
 
 test_expect_success 'revert fourth fourth~1 fourth~2 works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard fourth &&
 	test_tick &&
 	git revert fourth fourth~1 fourth~2 &&
@@ -174,7 +177,7 @@
 '
 
 test_expect_success 'cherry-pick -3 fourth works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git cherry-pick -3 fourth &&
@@ -184,7 +187,7 @@
 '
 
 test_expect_success 'cherry-pick --stdin works' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard first &&
 	test_tick &&
 	git rev-list --reverse first..fourth | git cherry-pick --stdin &&
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
index 1e5b394..f415924 100755
--- a/t/t3509-cherry-pick-merge-df.sh
+++ b/t/t3509-cherry-pick-merge-df.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Test cherry-pick with directory/file conflicts'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'Initialize repository' '
@@ -28,7 +31,7 @@
 
 test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git cherry-pick branch
 '
 
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 5b94fda..b76cb6d 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -170,7 +170,7 @@
 	git commit -a &&
 	test_path_is_missing .git/CHERRY_PICK_HEAD &&
 	test_must_fail git cherry-pick --skip 2>advice &&
-	test_i18ncmp expect advice
+	test_cmp expect advice
 '
 
 test_expect_success 'selectively advise --skip while launching another sequence' '
@@ -182,7 +182,7 @@
 	EOF
 	test_must_fail git cherry-pick picked..yetanotherpick &&
 	test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
-	test_i18ncmp expect advice &&
+	test_cmp expect advice &&
 	cat >expect <<-EOF &&
 	error: cherry-pick is already in progress
 	hint: try "git cherry-pick (--continue | --abort | --quit)"
@@ -190,7 +190,7 @@
 	EOF
 	git reset --merge &&
 	test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
-	test_i18ncmp expect advice
+	test_cmp expect advice
 '
 
 test_expect_success 'allow skipping commit but not abort for a new history' '
@@ -204,7 +204,7 @@
 	test_must_fail git cherry-pick anotherpick &&
 	test_must_fail git cherry-pick --abort 2>advice &&
 	git cherry-pick --skip &&
-	test_i18ncmp expect advice
+	test_cmp expect advice
 '
 
 test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index 6ece1d8..822f2d4 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -2,6 +2,9 @@
 
 test_description='cherry-pick can handle submodules'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-submodule-update.sh
 
@@ -35,7 +38,7 @@
 		git add a_file &&
 		git commit -m "modify a file" &&
 
-		git checkout master &&
+		git checkout main &&
 
 		git submodule add ../sub sub &&
 		git submodule update sub &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index efec8d1..bb9ef35 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -5,6 +5,9 @@
 
 test_description='Test of the various options to git rm.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Setup some files to be removed, some with funny characters
@@ -240,7 +243,7 @@
 	test_path_is_missing frotz/nitfol
 '
 
-test_expect_success 'choking "git rm" should not let it die with cruft' '
+choke_git_rm_setup() {
 	git reset -q --hard &&
 	test_when_finished "rm -f .git/index.lock && git reset -q --hard" &&
 	i=0 &&
@@ -249,12 +252,24 @@
 	do
 		echo "100644 $hash 0	some-file-$i"
 		i=$(( $i + 1 ))
-	done | git update-index --index-info &&
+	done | git update-index --index-info
+}
+
+test_expect_success 'choking "git rm" should not let it die with cruft (induce SIGPIPE)' '
+	choke_git_rm_setup &&
 	# git command is intentionally placed upstream of pipe to induce SIGPIPE
 	git rm -n "some-file-*" | : &&
 	test_path_is_missing .git/index.lock
 '
 
+
+test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
+	choke_git_rm_setup &&
+	OUT=$( ((trap "" PIPE; git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+	test_match_signal 13 "$OUT" &&
+	test_path_is_missing .git/index.lock
+'
+
 test_expect_success 'Resolving by removal is not a warning-worthy event' '
 	git reset -q --hard &&
 	test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
@@ -439,7 +454,7 @@
 	git add .gitmodules &&
 	echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
 	git rm submod >actual 2>actual.err &&
-	test_i18ncmp expect.err actual.err &&
+	test_cmp expect.err actual.err &&
 	test_path_is_missing submod &&
 	test_path_is_missing submod/.git &&
 	git status -s -uno >actual &&
@@ -483,16 +498,16 @@
 	echo 1 >nitfol &&
 	git add nitfol &&
 	git commit -m "added nitfol 1" &&
-	git checkout -b branch2 master &&
+	git checkout -b branch2 main &&
 	echo 2 >nitfol &&
 	git add nitfol &&
 	git commit -m "added nitfol 2" &&
-	git checkout -b conflict1 master &&
+	git checkout -b conflict1 main &&
 	git -C submod fetch &&
 	git -C submod checkout branch1 &&
 	git add submod &&
 	git commit -m "submod 1" &&
-	git checkout -b conflict2 master &&
+	git checkout -b conflict2 main &&
 	git -C submod checkout branch2 &&
 	git add submod &&
 	git commit -m "submod 2"
@@ -604,7 +619,7 @@
 '
 
 test_expect_success 'rm of a populated submodule with a .git directory migrates git dir' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard &&
 	git submodule update &&
 	(
@@ -717,7 +732,7 @@
 	git checkout HEAD^ &&
 	git submodule update &&
 	git checkout -q HEAD^ &&
-	git checkout -q master 2>actual &&
+	git checkout -q main 2>actual &&
 	test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
 	git status -s submod >actual &&
 	echo "?? submod/" >expected &&
@@ -809,7 +824,7 @@
 	echo content1 >foo.txt &&
 	echo content1 >bar.txt &&
 	test_must_fail git rm foo.txt bar.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm files with different staged content without hints' '
@@ -822,7 +837,7 @@
 	echo content2 >foo.txt &&
 	echo content2 >bar.txt &&
 	test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm file with local modification' '
@@ -834,7 +849,7 @@
 	git commit -m "testing rm 3" &&
 	echo content3 >foo.txt &&
 	test_must_fail git rm foo.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm file with local modification without hints' '
@@ -844,7 +859,7 @@
 	EOF
 	echo content4 >bar.txt &&
 	test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm file with changes in the index' '
@@ -857,7 +872,7 @@
 	echo content5 >foo.txt &&
 	git add foo.txt &&
 	test_must_fail git rm foo.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm file with changes in the index without hints' '
@@ -866,7 +881,7 @@
 	    foo.txt
 	EOF
 	test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm files with two different errors' '
@@ -885,7 +900,7 @@
 	echo content6 >bar1.txt &&
 	git add bar1.txt &&
 	test_must_fail git rm bar1.txt foo1.txt 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'rm empty string should fail' '
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index b7d4ba6..b3b122f 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -304,7 +304,7 @@
 	error: '"'empty/'"' does not have a commit checked out
 	fatal: adding files failed
 	EOF
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'git add --dry-run of existing changed file' "
@@ -320,7 +320,7 @@
 
 test_expect_success 'git add --dry-run of an existing file output' "
 	echo \"fatal: pathspec 'ignored-file' did not match any files\" >expect &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 "
 
 cat >expect.err <<\EOF
@@ -339,8 +339,8 @@
 '
 
 test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' '
-	test_i18ncmp expect.out actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.out actual.out &&
+	test_cmp expect.err actual.err
 '
 
 test_expect_success 'git add empty string should fail' '
@@ -386,6 +386,36 @@
 	! test -x foo4
 '
 
+test_expect_success 'git add --chmod fails with non regular files (but updates the other paths)' '
+	git reset --hard &&
+	test_ln_s_add foo foo3 &&
+	touch foo4 &&
+	test_must_fail git add --chmod=+x foo3 foo4 2>stderr &&
+	test_i18ngrep "cannot chmod +x .foo3." stderr &&
+	test_mode_in_index 120000 foo3 &&
+	test_mode_in_index 100755 foo4
+'
+
+test_expect_success 'git add --chmod honors --dry-run' '
+	git reset --hard &&
+	echo foo >foo4 &&
+	git add foo4 &&
+	git add --chmod=+x --dry-run foo4 &&
+	test_mode_in_index 100644 foo4
+'
+
+test_expect_success 'git add --chmod --dry-run reports error for non regular files' '
+	git reset --hard &&
+	test_ln_s_add foo foo4 &&
+	test_must_fail git add --chmod=+x --dry-run foo4 2>stderr &&
+	test_i18ngrep "cannot chmod +x .foo4." stderr
+'
+
+test_expect_success 'git add --chmod --dry-run reports error for unmatched pathspec' '
+	test_must_fail git add --chmod=+x --dry-run nonexistent 2>stderr &&
+	test_i18ngrep "pathspec .nonexistent. did not match any files" stderr
+'
+
 test_expect_success 'no file status change if no pathspec is given' '
 	>foo5 &&
 	>foo6 &&
@@ -409,11 +439,17 @@
 '
 
 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 . &&
-	test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
+	git init repo &&
+	(
+		cd repo &&
+		mkdir -p sub/dir &&
+		touch x y z sub/a sub/dir/b &&
+		git add -A &&
+		git add --chmod=+x . &&
+		test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
+		git add --chmod=-x . &&
+		test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
+	)
 '
 
 test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index cc3f434..2077146 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='add -i basic tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -367,7 +370,7 @@
 '
 
 # Test splitting the first patch, then adding both
-test_expect_success C_LOCALE_OUTPUT 'add first line works' '
+test_expect_success 'add first line works' '
 	git commit -am "clear local changes" &&
 	git apply patch &&
 	printf "%s\n" s y y | git add -p file 2>error |
@@ -549,8 +552,8 @@
 	test_commit non-conflict &&
 	git checkout -b side &&
 	test_commit side conflict.t &&
-	git checkout master &&
-	test_commit master conflict.t &&
+	git checkout main &&
+	test_commit main conflict.t &&
 	test_must_fail git merge side &&
 	echo changed >non-conflict.t &&
 	echo y | git add -p >output &&
@@ -849,6 +852,12 @@
 	cat >expected <<-\EOF &&
 	dirty-both-ways
 	dirty-head
+	EOF
+	test_cmp expected actual &&
+	git -C for-submodules diff-files --name-only --ignore-submodules=none >actual &&
+	cat >expected <<-\EOF &&
+	dirty-both-ways
+	dirty-head
 	dirty-otherwise
 	EOF
 	test_cmp expected actual &&
@@ -965,7 +974,7 @@
 	EOF
 	test_write_lines h | force_color git add -i >actual.colored &&
 	test_decode_color <actual.colored >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index d696aa4..60a666d 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -12,10 +12,27 @@
 # given in the expect.pat file.
 
 check_verify_failure () {
-	expect="$2"
+	test_expect_success "$1" "
+		test_must_fail git mktag <tag.sig 2>message &&
+		grep '$2' message &&
+		if test '$3' != '--no-strict'
+		then
+			test_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb
+			grep '$2' message.no-strict
+		fi
+	"
+}
+
+test_expect_mktag_success() {
 	test_expect_success "$1" '
-		( test_must_fail git mktag <tag.sig 2>message ) &&
-		grep "$expect" message
+		git hash-object -t tag -w --stdin <tag.sig >expected &&
+		git fsck --strict &&
+
+		git mktag <tag.sig >hash &&
+		test_cmp expected hash &&
+		test_when_finished "git update-ref -d refs/tags/mytag $(cat hash)" &&
+		git update-ref refs/tags/mytag $(cat hash) $(test_oid zero) &&
+		git fsck --strict
 	'
 }
 
@@ -23,10 +40,24 @@
 # first create a commit, so we have a valid object/type
 # for the tag.
 test_expect_success 'setup' '
-	echo Hello >A &&
-	git update-index --add A &&
-	git commit -m "Initial commit" &&
-	head=$(git rev-parse --verify HEAD)
+	test_commit A &&
+	test_commit B &&
+	head=$(git rev-parse --verify HEAD) &&
+	head_parent=$(git rev-parse --verify HEAD~) &&
+	tree=$(git rev-parse HEAD^{tree}) &&
+	blob=$(git rev-parse --verify HEAD:B.t)
+'
+
+test_expect_success 'basic usage' '
+	cat >tag.sig <<-EOF &&
+	object $head
+	type commit
+	tag mytag
+	tagger T A Gger <tagger@example.com> 1206478233 -0500
+	EOF
+	git mktag <tag.sig &&
+	git mktag --end-of-options <tag.sig &&
+	test_expect_code 129 git mktag --unknown-option
 '
 
 ############################################################
@@ -37,33 +68,33 @@
 EOF
 
 check_verify_failure 'Tag object length check' \
-	'^error: .*size wrong.*$'
+	'^error:.* missingObject:' 'strict'
 
 ############################################################
 #  2. object line label check
 
 cat >tag.sig <<EOF
-xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
+xxxxxx $head
 type tag
 tag mytag
 tagger . <> 0 +0000
 
 EOF
 
-check_verify_failure '"object" line label check' '^error: char0: .*"object "$'
+check_verify_failure '"object" line label check' '^error:.* missingObject:'
 
 ############################################################
-#  3. object line SHA1 check
+#  3. object line hash check
 
 cat >tag.sig <<EOF
-object zz9e9b33986b1c2670fff52c5067603117b3e895
+object $(echo $head | tr 0-9a-f z)
 type tag
 tag mytag
 tagger . <> 0 +0000
 
 EOF
 
-check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
+check_verify_failure '"object" line check' '^error:.* badObjectSha1:'
 
 ############################################################
 #  4. type line label check
@@ -76,7 +107,7 @@
 
 EOF
 
-check_verify_failure '"type" line label check' '^error: char.*: .*"\\ntype "$'
+check_verify_failure '"type" line label check' '^error:.* missingTypeEntry:'
 
 ############################################################
 #  5. type line eol check
@@ -84,7 +115,7 @@
 echo "object $head" >tag.sig
 printf "type tagsssssssssssssssssssssssssssssss" >>tag.sig
 
-check_verify_failure '"type" line eol check' '^error: char.*: .*"\\n"$'
+check_verify_failure '"type" line eol check' '^error:.* unterminatedHeader:'
 
 ############################################################
 #  6. tag line label check #1
@@ -98,7 +129,7 @@
 EOF
 
 check_verify_failure '"tag" line label check #1' \
-	'^error: char.*: no "tag " found$'
+	'^error:.* missingTagEntry:'
 
 ############################################################
 #  7. tag line label check #2
@@ -110,7 +141,7 @@
 EOF
 
 check_verify_failure '"tag" line label check #2' \
-	'^error: char.*: no "tag " found$'
+	'^error:.* badType:'
 
 ############################################################
 #  8. type line type-name length check
@@ -122,10 +153,32 @@
 EOF
 
 check_verify_failure '"type" line type-name length check' \
-	'^error: char.*: type too long$'
+	'^error:.* badType:'
 
 ############################################################
-#  9. verify object (SHA1/type) check
+#  9. verify object (hash/type) check
+
+cat >tag.sig <<EOF
+object $(test_oid deadbeef)
+type tag
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- correct type, nonexisting object' \
+	'^fatal: could not read tagged object'
+
+cat >tag.sig <<EOF
+object $head
+type tagggg
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- made-up type, valid object' \
+	'^error:.* badType:'
 
 cat >tag.sig <<EOF
 object $(test_oid deadbeef)
@@ -135,8 +188,48 @@
 
 EOF
 
-check_verify_failure 'verify object (SHA1/type) check' \
-	'^error: char7: could not verify object.*$'
+check_verify_failure 'verify object (hash/type) check -- made-up type, nonexisting object' \
+	'^error:.* badType:'
+
+cat >tag.sig <<EOF
+object $head
+type tree
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \
+	'^fatal: object.*tagged as.*tree.*but is.*commit'
+
+############################################################
+#  9.5. verify object (hash/type) check -- replacement
+
+test_expect_success 'setup replacement of commit -> commit and tree -> blob' '
+	git replace $head_parent $head &&
+	git replace -f $tree $blob
+'
+
+cat >tag.sig <<EOF
+object $head_parent
+type commit
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+test_expect_mktag_success 'tag to a commit replaced by another commit'
+
+cat >tag.sig <<EOF
+object $tree
+type tree
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (hash/type) check -- mismatched type, valid object' \
+	'^fatal: object.*tagged as.*tree.*but is.*blob'
 
 ############################################################
 # 10. verify tag-name check
@@ -150,7 +243,7 @@
 EOF
 
 check_verify_failure 'verify tag-name check' \
-	'^error: char.*: could not verify tag name$'
+	'^error:.* badTagName:' '--no-strict'
 
 ############################################################
 # 11. tagger line label check #1
@@ -164,7 +257,7 @@
 EOF
 
 check_verify_failure '"tagger" line label check #1' \
-	'^error: char.*: could not find "tagger "$'
+	'^error:.* missingTaggerEntry:' '--no-strict'
 
 ############################################################
 # 12. tagger line label check #2
@@ -179,10 +272,10 @@
 EOF
 
 check_verify_failure '"tagger" line label check #2' \
-	'^error: char.*: could not find "tagger "$'
+	'^error:.* missingTaggerEntry:' '--no-strict'
 
 ############################################################
-# 13. disallow missing tag author name
+# 13. allow missing tag author name like fsck
 
 cat >tag.sig <<EOF
 object $head
@@ -193,8 +286,7 @@
 This is filler
 EOF
 
-check_verify_failure 'disallow missing tag author name' \
-	'^error: char.*: missing tagger name$'
+test_expect_mktag_success 'allow missing tag author name'
 
 ############################################################
 # 14. disallow missing tag author name
@@ -209,7 +301,7 @@
 EOF
 
 check_verify_failure 'disallow malformed tagger' \
-	'^error: char.*: malformed tagger field$'
+	'^error:.* badEmail:' '--no-strict'
 
 ############################################################
 # 15. allow empty tag email
@@ -222,12 +314,10 @@
 
 EOF
 
-test_expect_success \
-    'allow empty tag email' \
-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+test_expect_mktag_success 'allow empty tag email'
 
 ############################################################
-# 16. disallow spaces in tag email
+# 16. allow spaces in tag email like fsck
 
 cat >tag.sig <<EOF
 object $head
@@ -237,8 +327,7 @@
 
 EOF
 
-check_verify_failure 'disallow spaces in tag email' \
-	'^error: char.*: malformed tagger field$'
+test_expect_mktag_success 'allow spaces in tag email like fsck'
 
 ############################################################
 # 17. disallow missing tag timestamp
@@ -252,7 +341,7 @@
 EOF
 
 check_verify_failure 'disallow missing tag timestamp' \
-	'^error: char.*: missing tag timestamp$'
+	'^error:.* badDate:'
 
 ############################################################
 # 18. detect invalid tag timestamp1
@@ -266,7 +355,7 @@
 EOF
 
 check_verify_failure 'detect invalid tag timestamp1' \
-	'^error: char.*: missing tag timestamp$'
+	'^error:.* badDate:'
 
 ############################################################
 # 19. detect invalid tag timestamp2
@@ -280,7 +369,7 @@
 EOF
 
 check_verify_failure 'detect invalid tag timestamp2' \
-	'^error: char.*: malformed tag timestamp$'
+	'^error:.* badDate:'
 
 ############################################################
 # 20. detect invalid tag timezone1
@@ -294,7 +383,7 @@
 EOF
 
 check_verify_failure 'detect invalid tag timezone1' \
-	'^error: char.*: malformed tag timezone$'
+	'^error:.* badTimezone:'
 
 ############################################################
 # 21. detect invalid tag timezone2
@@ -308,10 +397,10 @@
 EOF
 
 check_verify_failure 'detect invalid tag timezone2' \
-	'^error: char.*: malformed tag timezone$'
+	'^error:.* badTimezone:'
 
 ############################################################
-# 22. detect invalid tag timezone3
+# 22. allow invalid tag timezone3 (the maximum is -1200/+1400)
 
 cat >tag.sig <<EOF
 object $head
@@ -321,8 +410,7 @@
 
 EOF
 
-check_verify_failure 'detect invalid tag timezone3' \
-	'^error: char.*: malformed tag timezone$'
+test_expect_mktag_success 'allow invalid tag timezone'
 
 ############################################################
 # 23. detect invalid header entry
@@ -337,10 +425,39 @@
 EOF
 
 check_verify_failure 'detect invalid header entry' \
-	'^error: char.*: trailing garbage in tag header$'
+	'^error:.* extraHeaderEntry:' '--no-strict'
 
-############################################################
-# 24. create valid tag
+test_expect_success 'invalid header entry config & fsck' '
+	test_must_fail git mktag <tag.sig &&
+	git mktag --no-strict <tag.sig &&
+
+	test_must_fail git -c fsck.extraHeaderEntry=error mktag <tag.sig &&
+	test_must_fail git -c fsck.extraHeaderEntry=error mktag --no-strict <tag.sig &&
+
+	test_must_fail git -c fsck.extraHeaderEntry=warn mktag <tag.sig &&
+	git -c fsck.extraHeaderEntry=warn mktag --no-strict <tag.sig &&
+
+	git -c fsck.extraHeaderEntry=ignore mktag <tag.sig &&
+	git -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&
+
+	git fsck &&
+	git -c fsck.extraHeaderEntry=warn fsck 2>err &&
+	grep "warning .*extraHeaderEntry:" err &&
+	test_must_fail git -c fsck.extraHeaderEntry=error 2>err fsck &&
+	grep "error .* extraHeaderEntry:" err
+'
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+
+
+this line comes after an extra newline
+EOF
+
+test_expect_mktag_success 'allow extra newlines at start of body'
 
 cat >tag.sig <<EOF
 object $head
@@ -350,16 +467,27 @@
 
 EOF
 
-test_expect_success \
-    'create valid tag' \
-    'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+test_expect_mktag_success 'allow a blank line before an empty body (1)'
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+EOF
+
+test_expect_mktag_success 'allow no blank line before an empty body (2)'
 
 ############################################################
-# 25. check mytag
+# 24. create valid tag
 
-test_expect_success \
-    'check mytag' \
-    'git tag -l | grep mytag'
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+EOF
 
+test_expect_mktag_success 'create valid tag object'
 
 test_done
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 923eb01..4f16a73 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -5,6 +5,9 @@
 
 test_description='i18n settings and format-patch | am pipe'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_encoding () {
@@ -45,7 +48,7 @@
 
 	# the first commit on the side branch is UTF-8
 	test_tick &&
-	git checkout -b side master^ &&
+	git checkout -b side main^ &&
 	echo Another file >yours &&
 	git add yours &&
 	git commit -s -m "Second on side" &&
@@ -69,7 +72,7 @@
 test_expect_success 'format-patch output (ISO-8859-1)' '
 	git config i18n.logoutputencoding ISO8859-1 &&
 
-	git format-patch --stdout master..HEAD^ >out-l1 &&
+	git format-patch --stdout main..HEAD^ >out-l1 &&
 	git format-patch --stdout HEAD^ >out-l2 &&
 	grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 &&
 	grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 &&
@@ -80,7 +83,7 @@
 test_expect_success 'format-patch output (UTF-8)' '
 	git config i18n.logoutputencoding UTF-8 &&
 
-	git format-patch --stdout master..HEAD^ >out-u1 &&
+	git format-patch --stdout main..HEAD^ >out-u1 &&
 	git format-patch --stdout HEAD^ >out-u2 &&
 	grep "^Content-Type: text/plain; charset=UTF-8" out-u1 &&
 	grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u1 &&
@@ -102,7 +105,7 @@
 	# we want UTF-8 encoded name.
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 	git checkout -b test &&
-	git rebase master &&
+	git rebase main &&
 
 	check_encoding 2
 '
@@ -113,7 +116,7 @@
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
 	git reset --hard side &&
-	git rebase master &&
+	git rebase main &&
 
 	check_encoding 2
 '
@@ -125,7 +128,7 @@
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
 	git reset --hard side &&
-	git rebase master &&
+	git rebase main &&
 
 	check_encoding 2 8859
 '
@@ -138,7 +141,7 @@
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
 	git reset --hard side &&
-	git rebase master &&
+	git rebase main &&
 
 	check_encoding 2 8859
 '
@@ -150,7 +153,7 @@
 	git config i18n.logoutputencoding UTF-8 &&
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
 	git revert HEAD &&
@@ -165,7 +168,7 @@
 	git config i18n.logoutputencoding ISO8859-1 &&
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
 	git revert HEAD &&
@@ -180,7 +183,7 @@
 	git config i18n.logoutputencoding ISO8859-1 &&
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
 	git revert HEAD &&
@@ -196,7 +199,7 @@
 	git config i18n.logoutputencoding UTF-8 &&
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git cherry-pick side^ &&
 	git cherry-pick side &&
 	git revert HEAD &&
@@ -210,7 +213,7 @@
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
 	git reset --hard side &&
-	git rebase --merge master &&
+	git rebase --merge main &&
 
 	check_encoding 2
 '
@@ -221,7 +224,7 @@
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
 	git reset --hard side &&
-	git rebase --merge master &&
+	git rebase --merge main &&
 
 	check_encoding 2
 '
@@ -233,7 +236,7 @@
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
 	git reset --hard side &&
-	git rebase --merge master &&
+	git rebase --merge main &&
 
 	check_encoding 2 8859
 '
@@ -246,7 +249,7 @@
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
 	git reset --hard side &&
-	git rebase --merge master &&
+	git rebase --merge main &&
 
 	check_encoding 2 8859
 '
@@ -256,7 +259,7 @@
 	git config i18n.commitencoding UTF-8 &&
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git am out-u1 out-u2 &&
 
 	check_encoding 2
@@ -267,7 +270,7 @@
 	git config i18n.commitencoding ISO8859-1 &&
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git am out-l1 out-l2 &&
 
 	check_encoding 2 8859
@@ -277,7 +280,7 @@
 	# Apply ISO-8859-1 patches with UTF-8 commitencoding
 	git config i18n.commitencoding UTF-8 &&
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
-	git reset --hard master &&
+	git reset --hard main &&
 
 	# am specifies --utf8 by default.
 	git am out-l1 out-l2 &&
@@ -290,7 +293,7 @@
 	git config i18n.commitencoding UTF-8 &&
 	. "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git am --no-utf8 out-l1 out-l2 2>err &&
 
 	# commit-tree will warn that the commit message does not contain valid UTF-8
@@ -305,7 +308,7 @@
 	git config i18n.commitencoding ISO8859-1 &&
 	. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	# mailinfo will re-code the commit message to the charset specified by
 	# i18n.commitencoding
 	git am out-u1 out-u2 &&
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 9f7ca98..5f282ec 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -5,6 +5,9 @@
 
 test_description='Test git stash'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 diff_cmp () {
@@ -220,14 +223,14 @@
 	git commit file -m second &&
 	git stash branch stashbranch &&
 	test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
-	test $(git rev-parse HEAD) = $(git rev-parse master^) &&
+	test $(git rev-parse HEAD) = $(git rev-parse main^) &&
 	git diff --cached >output &&
 	diff_cmp expect output &&
 	git diff >output &&
 	diff_cmp expect1 output &&
 	git add file &&
 	git commit -m alternate\ second &&
-	git diff master..stashbranch >output &&
+	git diff main..stashbranch >output &&
 	diff_cmp output expect2 &&
 	test 0 = $(git stash list | wc -l)
 '
@@ -520,7 +523,7 @@
 	STASH_ID=$(git stash create) &&
 	git reset --hard &&
 	git stash branch stash-branch ${STASH_ID} &&
-	test_when_finished "git reset --hard HEAD && git checkout master &&
+	test_when_finished "git reset --hard HEAD && git checkout main &&
 	git branch -D stash-branch" &&
 	test $(git ls-files --modified | wc -l) -eq 1
 '
@@ -536,7 +539,7 @@
 	STASH_ID=$(git stash create) &&
 	git reset --hard &&
 	git stash branch stash-branch ${STASH_ID} &&
-	test_when_finished "git reset --hard HEAD && git checkout master &&
+	test_when_finished "git reset --hard HEAD && git checkout main &&
 	git branch -D stash-branch" &&
 	test $(git ls-files --modified | wc -l) -eq 1
 '
@@ -561,7 +564,7 @@
 	 1 file changed, 1 insertion(+)
 	EOF
 	git stash show ${STASH_ID} >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'stash show - stashes on stack, stash-like argument' '
@@ -738,7 +741,7 @@
 	git stash &&
 	git stash show 0 &&
 	git stash branch tmp 0 &&
-	git checkout master &&
+	git checkout main &&
 	git stash &&
 	git stash apply 0 &&
 	git reset --hard &&
@@ -755,7 +758,7 @@
 	git commit -m initial &&
 	echo bar >file &&
 	git stash &&
-	test_must_fail git stash branch master stash@{0} &&
+	test_must_fail git stash branch main stash@{0} &&
 	git rev-parse stash@{0} --
 '
 
@@ -768,7 +771,7 @@
 	echo bar >file &&
 	git stash &&
 	echo baz >file &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	test_must_fail git stash branch new_branch stash@{0} &&
 	git rev-parse stash@{0} --
 '
@@ -789,7 +792,7 @@
 		git stash apply
 	) |
 	sed -e 1d >actual && # drop "Saved..."
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<EOF
@@ -902,7 +905,7 @@
 	>foo &&
 	git add foo &&
 	git stash push -m "test message" &&
-	echo "stash@{0}: On master: test message" >expect &&
+	echo "stash@{0}: On main: test message" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -911,7 +914,7 @@
 	>foo &&
 	git add foo &&
 	git stash push -m"unspaced test message" &&
-	echo "stash@{0}: On master: unspaced test message" >expect &&
+	echo "stash@{0}: On main: unspaced test message" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -968,7 +971,7 @@
 	>foo &&
 	git add foo &&
 	git stash push -m"test mfoo" &&
-	echo "stash@{0}: On master: test mfoo" >expect &&
+	echo "stash@{0}: On main: test mfoo" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -977,7 +980,7 @@
 	>foo &&
 	git add foo &&
 	git stash push --message "test message foo" &&
-	echo "stash@{0}: On master: test message foo" >expect &&
+	echo "stash@{0}: On main: test message foo" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -986,7 +989,7 @@
 	>foo &&
 	git add foo &&
 	git stash push --message="test message=foo" &&
-	echo "stash@{0}: On master: test message=foo" >expect &&
+	echo "stash@{0}: On main: test message=foo" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -995,7 +998,7 @@
 	>foo &&
 	git add foo &&
 	git stash push -m "test m foo" &&
-	echo "stash@{0}: On master: test m foo" >expect &&
+	echo "stash@{0}: On main: test m foo" >expect &&
 	git stash list -1 >actual &&
 	test_cmp expect actual
 '
@@ -1004,7 +1007,7 @@
 	>foo &&
 	git add foo &&
 	STASH_ID=$(git stash create "create test message") &&
-	echo "On master: create test message" >expect &&
+	echo "On main: create test message" >expect &&
 	git show --pretty=%s -s ${STASH_ID} >actual &&
 	test_cmp expect actual
 '
@@ -1013,13 +1016,13 @@
 	>foo &&
 	git add foo &&
 	STASH_ID=$(git stash create test untracked) &&
-	echo "On master: test untracked" >expect &&
+	echo "On main: test untracked" >expect &&
 	git show --pretty=%s -s ${STASH_ID} >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'create in a detached state' '
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git checkout HEAD~1 &&
 	>foo &&
 	git add foo &&
@@ -1111,7 +1114,7 @@
 	git stash push -p foo >actual &&
 	echo "No local changes to save" >expect &&
 	git reset --hard HEAD~ &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'push <pathspec>: show no changes when there are none' '
@@ -1121,7 +1124,7 @@
 	git stash push foo >actual &&
 	echo "No local changes to save" >expect &&
 	git reset --hard HEAD~ &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'push: <pathspec> not in the repository errors out' '
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index f075c7f..598b17f 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -8,16 +8,16 @@
 . ./test-lib.sh
 
 test_expect_success 'stash save --include-untracked some dirty working directory' '
-	echo 1 > file &&
+	echo 1 >file &&
 	git add file &&
 	test_tick &&
 	git commit -m initial &&
-	echo 2 > file &&
+	echo 2 >file &&
 	git add file &&
-	echo 3 > file &&
+	echo 3 >file &&
 	test_tick &&
-	echo 1 > file2 &&
-	echo 1 > HEAD &&
+	echo 1 >file2 &&
+	echo 1 >HEAD &&
 	mkdir untracked &&
 	echo untracked >untracked/untracked &&
 	git stash --include-untracked &&
@@ -25,48 +25,50 @@
 	git diff-index --cached --quiet HEAD
 '
 
-cat > expect <<EOF
-?? actual
-?? expect
-EOF
-
 test_expect_success 'stash save --include-untracked cleaned the untracked files' '
+	cat >expect <<-EOF &&
+	?? actual
+	?? expect
+	EOF
+
 	git status --porcelain >actual &&
 	test_cmp expect actual
 '
 
-tracked=$(git rev-parse --short $(echo 1 | git hash-object --stdin))
-untracked=$(git rev-parse --short $(echo untracked | git hash-object --stdin))
-cat > expect.diff <<EOF
-diff --git a/HEAD b/HEAD
-new file mode 100644
-index 0000000..$tracked
---- /dev/null
-+++ b/HEAD
-@@ -0,0 +1 @@
-+1
-diff --git a/file2 b/file2
-new file mode 100644
-index 0000000..$tracked
---- /dev/null
-+++ b/file2
-@@ -0,0 +1 @@
-+1
-diff --git a/untracked/untracked b/untracked/untracked
-new file mode 100644
-index 0000000..$untracked
---- /dev/null
-+++ b/untracked/untracked
-@@ -0,0 +1 @@
-+untracked
-EOF
-cat > expect.lstree <<EOF
-HEAD
-file2
-untracked
-EOF
-
 test_expect_success 'stash save --include-untracked stashed the untracked files' '
+	one_blob=$(echo 1 | git hash-object --stdin) &&
+	tracked=$(git rev-parse --short "$one_blob") &&
+	untracked_blob=$(echo untracked | git hash-object --stdin) &&
+	untracked=$(git rev-parse --short "$untracked_blob") &&
+	cat >expect.diff <<-EOF &&
+	diff --git a/HEAD b/HEAD
+	new file mode 100644
+	index 0000000..$tracked
+	--- /dev/null
+	+++ b/HEAD
+	@@ -0,0 +1 @@
+	+1
+	diff --git a/file2 b/file2
+	new file mode 100644
+	index 0000000..$tracked
+	--- /dev/null
+	+++ b/file2
+	@@ -0,0 +1 @@
+	+1
+	diff --git a/untracked/untracked b/untracked/untracked
+	new file mode 100644
+	index 0000000..$untracked
+	--- /dev/null
+	+++ b/untracked/untracked
+	@@ -0,0 +1 @@
+	+untracked
+	EOF
+	cat >expect.lstree <<-EOF &&
+	HEAD
+	file2
+	untracked
+	EOF
+
 	test_path_is_missing file2 &&
 	test_path_is_missing untracked &&
 	test_path_is_missing HEAD &&
@@ -83,57 +85,64 @@
 	test_must_fail git stash --patch --all
 '
 
-git clean --force --quiet
+test_expect_success 'clean up untracked/untracked file to prepare for next tests' '
+	git clean --force --quiet
 
-cat > expect <<EOF
- M file
-?? HEAD
-?? actual
-?? expect
-?? file2
-?? untracked/
-EOF
+'
 
 test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '
+	cat >expect <<-EOF &&
+	 M file
+	?? HEAD
+	?? actual
+	?? expect
+	?? file2
+	?? untracked/
+	EOF
+
 	git stash pop &&
 	git status --porcelain >actual &&
 	test_cmp expect actual &&
-	test "1" = "$(cat file2)" &&
-	test untracked = "$(cat untracked/untracked)"
+	echo 1 >expect_file2 &&
+	test_cmp expect_file2 file2 &&
+	echo untracked >untracked_expect &&
+	test_cmp untracked_expect untracked/untracked
 '
 
-git clean --force --quiet -d
+test_expect_success 'clean up untracked/ directory to prepare for next tests' '
+	git clean --force --quiet -d
+'
 
 test_expect_success 'stash save -u dirty index' '
-	echo 4 > file3 &&
+	echo 4 >file3 &&
 	git add file3 &&
 	test_tick &&
 	git stash -u
 '
 
-blob=$(git rev-parse --short $(echo 4 | git hash-object --stdin))
-cat > expect <<EOF
-diff --git a/file3 b/file3
-new file mode 100644
-index 0000000..$blob
---- /dev/null
-+++ b/file3
-@@ -0,0 +1 @@
-+4
-EOF
-
 test_expect_success 'stash save --include-untracked dirty index got stashed' '
+	four_blob=$(echo 4 | git hash-object --stdin) &&
+	blob=$(git rev-parse --short "$four_blob") &&
+	cat >expect <<-EOF &&
+	diff --git a/file3 b/file3
+	new file mode 100644
+	index 0000000..$blob
+	--- /dev/null
+	+++ b/file3
+	@@ -0,0 +1 @@
+	+4
+	EOF
+
 	git stash pop --index &&
+	test_when_finished "git reset" &&
 	git diff --cached >actual &&
 	test_cmp expect actual
 '
 
-git reset > /dev/null
-
 # Must direct output somewhere where it won't be considered an untracked file
 test_expect_success 'stash save --include-untracked -q is quiet' '
-	echo 1 > file5 &&
-	git stash save --include-untracked --quiet > .git/stash-output.out 2>&1 &&
+	echo 1 >file5 &&
+	git stash save --include-untracked --quiet >.git/stash-output.out 2>&1 &&
 	test_line_count = 0 .git/stash-output.out &&
 	rm -f .git/stash-output.out
 '
@@ -141,35 +150,34 @@
 test_expect_success 'stash save --include-untracked removed files' '
 	rm -f file &&
 	git stash save --include-untracked &&
-	echo 1 > expect &&
+	echo 1 >expect &&
+	test_when_finished "rm -f expect" &&
 	test_cmp expect file
 '
 
-rm -f expect
-
 test_expect_success 'stash save --include-untracked removed files got stashed' '
 	git stash pop &&
 	test_path_is_missing file
 '
 
-cat > .gitignore <<EOF
-.gitignore
-ignored
-ignored.d/
-EOF
-
 test_expect_success 'stash save --include-untracked respects .gitignore' '
-	echo ignored > ignored &&
+	cat >.gitignore <<-EOF &&
+	.gitignore
+	ignored
+	ignored.d/
+	EOF
+
+	echo ignored >ignored &&
 	mkdir ignored.d &&
 	echo ignored >ignored.d/untracked &&
 	git stash -u &&
-	test -s ignored &&
-	test -s ignored.d/untracked &&
-	test -s .gitignore
+	test_file_not_empty ignored &&
+	test_file_not_empty ignored.d/untracked &&
+	test_file_not_empty .gitignore
 '
 
 test_expect_success 'stash save -u can stash with only untracked files different' '
-	echo 4 > file4 &&
+	echo 4 >file4 &&
 	git stash -u &&
 	test_path_is_missing file4
 '
@@ -183,9 +191,9 @@
 
 test_expect_success 'stash save --all is stash poppable' '
 	git stash pop &&
-	test -s ignored &&
-	test -s ignored.d/untracked &&
-	test -s .gitignore
+	test_file_not_empty ignored &&
+	test_file_not_empty ignored.d/untracked &&
+	test_file_not_empty .gitignore
 '
 
 test_expect_success 'stash push --include-untracked with pathspec' '
@@ -214,17 +222,17 @@
 	test_path_is_file bar
 '
 
-cat > .gitignore <<EOF
-ignored
-ignored.d/*
-EOF
-
 test_expect_success 'stash previously ignored file' '
+	cat >.gitignore <<-EOF &&
+	ignored
+	ignored.d/*
+	EOF
+
 	git reset HEAD &&
 	git add .gitignore &&
 	git commit -m "Add .gitignore" &&
 	>ignored.d/foo &&
-	echo "!ignored.d/foo" >> .gitignore &&
+	echo "!ignored.d/foo" >>.gitignore &&
 	git stash save --include-untracked &&
 	test_path_is_missing ignored.d/foo &&
 	git stash pop &&
@@ -280,7 +288,7 @@
 test_expect_success 'stash -u -- <non-existent> shows no changes when there are none' '
 	git stash push -u -- non-existent >actual &&
 	echo "No local changes to save" >expect &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'stash -u with globs' '
diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh
index 54ce19e..898267a 100755
--- a/t/t3910-mac-os-precompose.sh
+++ b/t/t3910-mac-os-precompose.sh
@@ -5,6 +5,9 @@
 
 test_description='utf-8 decomposed (nfd) converted to precomposed (nfc)'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq UTF8_NFD_TO_NFC
@@ -151,7 +154,7 @@
 	git checkout l.$Odiarnfd
 '
 test_expect_success "setup case mac2" '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard &&
 	git checkout -b mac_os_2
 '
@@ -163,7 +166,7 @@
 	git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd
 '
 test_expect_success "setup for long decomposed filename" '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard &&
 	git checkout -b mac_os_long_nfd_fn
 '
@@ -173,7 +176,7 @@
 	git commit -m "Long filename"
 '
 test_expect_success "setup for long precomposed filename" '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard &&
 	git checkout -b mac_os_long_nfc_fn
 '
@@ -191,6 +194,22 @@
 	test_must_be_empty untracked
 '
 
+test_expect_success "unicode decomposed: git restore -p . " '
+	DIRNAMEPWD=dir.Odiarnfc &&
+	DIRNAMEINREPO=dir.$Adiarnfc &&
+	export DIRNAMEPWD DIRNAMEINREPO &&
+	git init "$DIRNAMEPWD" &&
+	(
+		cd "$DIRNAMEPWD" &&
+		mkdir "$DIRNAMEINREPO" &&
+		cd "$DIRNAMEINREPO" &&
+		echo "Initial" >file &&
+		git add file &&
+		echo "More stuff" >>file &&
+		echo y | git restore -p .
+	)
+'
+
 # Test if the global core.precomposeunicode stops autosensing
 # Must be the last test case
 test_expect_success "respect git config --global core.precomposeunicode" '
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index e5116a7..cce3349 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index c16486a..68f2ebc 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success 'setup' '
 	cat >path0 <<-\EOF &&
@@ -262,4 +262,28 @@
 	grep "myotherfile.*myfile" actual
 '
 
+test_expect_success 'basename similarity vs best similarity' '
+	mkdir subdir &&
+	test_write_lines line1 line2 line3 line4 line5 \
+			 line6 line7 line8 line9 line10 >subdir/file.txt &&
+	git add subdir/file.txt &&
+	git commit -m "base txt" &&
+
+	git rm subdir/file.txt &&
+	test_write_lines line1 line2 line3 line4 line5 \
+			  line6 line7 line8 >file.txt &&
+	test_write_lines line1 line2 line3 line4 line5 \
+			  line6 line7 line8 line9 >file.md &&
+	git add file.txt file.md &&
+	git commit -a -m "rename" &&
+	git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
+	# subdir/file.txt is 88% similar to file.md, 78% similar to file.txt,
+	# but since same basenames are checked first...
+	cat >expected <<-\EOF &&
+	A	file.md
+	R078	subdir/file.txt	file.txt
+	EOF
+	test_cmp expected actual
+'
+
 test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index df2accb..db07ff3 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 6e562c8..3d495e3 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -10,7 +10,7 @@
 by an edit for them.
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success SYMLINKS \
     'prepare reference tree' \
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index d18a804..8647906 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -6,10 +6,10 @@
 test_description='Same rename detection as t4003 but testing diff-raw.'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success 'setup reference tree' '
-	cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+	cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
 	echo frotz >rezrov &&
 	git update-index --add COPYING rezrov &&
 	tree=$(git write-tree) &&
@@ -64,7 +64,7 @@
 # nows how to say Copy.
 
 test_expect_success 'validate output from rename/copy detection (#3)' '
-	cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+	cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
 	git update-index --add --remove COPYING COPYING.1 &&
 
 	cat <<-EOF >expected &&
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
index 03489af..275ce5f 100755
--- a/t/t4006-diff-mode.sh
+++ b/t/t4006-diff-mode.sh
@@ -39,13 +39,13 @@
 	 1 file changed, 0 insertions(+), 0 deletions(-)
 	EOF
 	git diff HEAD --stat >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success '--shortstat output after text chmod' '
 	tail -n 1 <expect >expect.short &&
 	git diff HEAD --shortstat >actual &&
-	test_i18ncmp expect.short actual
+	test_cmp expect.short actual
 '
 
 test_expect_success '--stat output after binary chmod' '
@@ -56,13 +56,13 @@
 	 2 files changed, 0 insertions(+), 0 deletions(-)
 	EOF
 	git diff HEAD --stat >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success '--shortstat output after binary chmod' '
 	tail -n 1 <expect >expect.short &&
 	git diff HEAD --shortstat >actual &&
-	test_i18ncmp expect.short actual
+	test_cmp expect.short actual
 '
 
 test_done
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index b187b7f..cbb9c62 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -7,17 +7,17 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success 'prepare reference tree' '
 	mkdir path0 path1 &&
-	cp "$TEST_DIRECTORY"/diff-lib/COPYING path0/COPYING &&
+	cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING &&
 	git update-index --add path0/COPYING &&
 	tree=$(git write-tree) &&
 	echo $tree
 '
 
-blob=$(git hash-object "$TEST_DIRECTORY/diff-lib/COPYING")
+blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING")
 test_expect_success 'prepare work tree' '
 	cp path0/COPYING path1/COPYING &&
 	git update-index --add --remove path0/COPYING path1/COPYING
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index b1ccd41..2299f27 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -22,11 +22,11 @@
 Further, with -B and -M together, these should turn into two renames.
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success setup '
-	cat "$TEST_DIRECTORY"/diff-lib/README >file0 &&
-	cat "$TEST_DIRECTORY"/diff-lib/COPYING >file1 &&
+	cat "$TEST_DIRECTORY"/lib-diff/README >file0 &&
+	cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 &&
 	blob0_id=$(git hash-object file0) &&
 	blob1_id=$(git hash-object file1) &&
 	git update-index --add file0 file1 &&
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index b63bdf0..b1da807 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     orig=$(git hash-object COPYING) &&
@@ -81,7 +81,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 65cc703..1bbced7 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -10,7 +10,7 @@
         path1/file1
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index 717034b..5a25c25 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 # Print the short OID of a symlink with the given name.
 symlink_oid () {
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index 6579c81..33ff588 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -34,19 +34,19 @@
 test_expect_success 'apply --stat output for binary file change' '
 	git diff >diff &&
 	git apply --stat --summary <diff >current &&
-	test_i18ncmp expected current
+	test_cmp expected current
 '
 
 test_expect_success 'diff --shortstat output for binary file change' '
 	tail -n 1 expected >expect &&
 	git diff --shortstat >current &&
-	test_i18ncmp expect current
+	test_cmp expect current
 '
 
 test_expect_success 'diff --shortstat output for binary file change only' '
 	echo " 1 file changed, 0 insertions(+), 0 deletions(-)" >expected &&
 	git diff --shortstat -- b >current &&
-	test_i18ncmp expected current
+	test_cmp expected current
 '
 
 test_expect_success 'apply --numstat notices binary file change' '
@@ -63,7 +63,7 @@
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+test_expect_success 'apply detecting corrupt patch correctly' '
 	git diff >output &&
 	sed -e "s/-CIT/xCIT/" <output >broken &&
 	test_must_fail git apply --stat --summary broken 2>detected &&
@@ -73,7 +73,7 @@
 	test "$detected" = xCIT
 '
 
-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+test_expect_success 'apply detecting corrupt patch correctly' '
 	git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
 	test_must_fail git apply --stat --summary broken 2>detected &&
 	detected=$(cat detected) &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index f72d456..6cca8b8 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -5,8 +5,11 @@
 
 test_description='Various diff formatting options'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success setup '
 
@@ -175,6 +178,7 @@
 V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
 while read magic cmd
 do
+	status=success
 	case "$magic" in
 	'' | '#'*)
 		continue ;;
@@ -183,6 +187,10 @@
 		label="$magic-$cmd"
 		case "$magic" in
 		noellipses) ;;
+		failure)
+			status=failure
+			magic=
+			label="$cmd" ;;
 		*)
 			BUG "unknown magic $magic" ;;
 		esac ;;
@@ -195,7 +203,7 @@
 	expect="$TEST_DIRECTORY/t4013/diff.$test"
 	actual="$pfx-diff.$test"
 
-	test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
+	test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
 		{
 			echo "$ git $cmd"
 			case "$magic" in
@@ -214,7 +222,7 @@
 			process_diffs "$expect" >expect &&
 			case $cmd in
 			*format-patch* | *-stat*)
-				test_i18ncmp expect actual;;
+				test_cmp expect actual;;
 			*)
 				test_cmp expect actual;;
 			esac &&
@@ -323,8 +331,12 @@
 log --diff-merges=off -p --first-parent master
 log --first-parent --diff-merges=off -p master
 log -p --first-parent master
+log -p --diff-merges=first-parent master
+log --diff-merges=first-parent master
 log -m -p --first-parent master
 log -m -p master
+log --cc -m -p master
+log -c -m -p master
 log -SF master
 log -S F master
 log -SF -p master
diff --git a/t/t4013/diff.log_--cc_-m_-p_master b/t/t4013/diff.log_--cc_-m_-p_master
new file mode 100644
index 0000000..7c217cf
--- /dev/null
+++ b/t/t4013/diff.log_--cc_-m_-p_master
@@ -0,0 +1,200 @@
+$ git log --cc -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--diff-merges=first-parent_master b/t/t4013/diff.log_--diff-merges=first-parent_master
new file mode 100644
index 0000000..fa63a55
--- /dev/null
+++ b/t/t4013/diff.log_--diff-merges=first-parent_master
@@ -0,0 +1,56 @@
+$ git log --diff-merges=first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-c_-m_-p_master b/t/t4013/diff.log_-c_-m_-p_master
new file mode 100644
index 0000000..b660f3d
--- /dev/null
+++ b/t/t4013/diff.log_-c_-m_-p_master
@@ -0,0 +1,200 @@
+$ git log -c -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-p_--diff-merges=first-parent_master b/t/t4013/diff.log_-p_--diff-merges=first-parent_master
new file mode 100644
index 0000000..9538a27
--- /dev/null
+++ b/t/t4013/diff.log_-p_--diff-merges=first-parent_master
@@ -0,0 +1,137 @@
+$ git log -p --diff-merges=first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index c5e5e0d..cdd3154 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -5,6 +5,9 @@
 
 test_description='various format-patch tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -32,11 +35,11 @@
 	test_tick &&
 	git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
-	git checkout master &&
+	git checkout main &&
 	git diff-tree -p C2 >patch &&
 	git apply --index <patch &&
 	test_tick &&
-	git commit -m "Master accepts moral equivalent of #2" &&
+	git commit -m "Main accepts moral equivalent of #2" &&
 
 	git checkout side &&
 	git checkout -b patchid &&
@@ -56,39 +59,39 @@
 	test_tick &&
 	git commit -m "patchid 3" &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream' '
-	git format-patch --stdout master..side >patch0 &&
+	git format-patch --stdout main..side >patch0 &&
 	grep "^From " patch0 >from0 &&
 	test_line_count = 3 from0
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream' '
 	git format-patch --stdout \
-		--ignore-if-in-upstream master..side >patch1 &&
+		--ignore-if-in-upstream main..side >patch1 &&
 	grep "^From " patch1 >from1 &&
 	test_line_count = 2 from1
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream handles tags' '
 	git tag -a v1 -m tag side &&
-	git tag -a v2 -m tag master &&
+	git tag -a v2 -m tag main &&
 	git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 &&
 	grep "^From " patch1 >from1 &&
 	test_line_count = 2 from1
 '
 
 test_expect_success "format-patch doesn't consider merge commits" '
-	git checkout -b feature master &&
+	git checkout -b feature main &&
 	echo "Another line" >>file &&
 	test_tick &&
 	git commit -am "Feature branch change #1" &&
 	echo "Yet another line" >>file &&
 	test_tick &&
 	git commit -am "Feature branch change #2" &&
-	git checkout -b merger master &&
+	git checkout -b merger main &&
 	test_tick &&
 	git merge --no-ff feature &&
 	git format-patch -3 --stdout >patch &&
@@ -97,16 +100,16 @@
 '
 
 test_expect_success 'format-patch result applies' '
-	git checkout -b rebuild-0 master &&
+	git checkout -b rebuild-0 main &&
 	git am -3 patch0 &&
-	git rev-list master.. >list &&
+	git rev-list main.. >list &&
 	test_line_count = 2 list
 '
 
 test_expect_success 'format-patch --ignore-if-in-upstream result applies' '
-	git checkout -b rebuild-1 master &&
+	git checkout -b rebuild-1 main &&
 	git am -3 patch1 &&
-	git rev-list master.. >list &&
+	git rev-list main.. >list &&
 	test_line_count = 2 list
 '
 
@@ -130,7 +133,7 @@
 " &&
 	git config --add format.headers "Cc: S E Cipient <scipient@example.com>
 " &&
-	git format-patch --stdout master..side >patch2 &&
+	git format-patch --stdout main..side >patch2 &&
 	sed -e "/^\$/q" patch2 >hdrs2 &&
 	grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 &&
 	grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2
@@ -139,7 +142,7 @@
 test_expect_success 'extra headers without newlines' '
 	git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
 	git config --add format.headers "Cc: S E Cipient <scipient@example.com>" &&
-	git format-patch --stdout master..side >patch3 &&
+	git format-patch --stdout main..side >patch3 &&
 	sed -e "/^\$/q" patch3 >hdrs3 &&
 	grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 &&
 	grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3
@@ -148,7 +151,7 @@
 test_expect_success 'extra headers with multiple To:s' '
 	git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
 	git config --add format.headers "To: S E Cipient <scipient@example.com>" &&
-	git format-patch --stdout master..side >patch4 &&
+	git format-patch --stdout main..side >patch4 &&
 	sed -e "/^\$/q" patch4 >hdrs4 &&
 	grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 &&
 	grep "^ *S E Cipient <scipient@example.com>\$" hdrs4
@@ -156,7 +159,7 @@
 
 test_expect_success 'additional command line cc (ascii)' '
 	git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-	git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+	git format-patch --cc="S E Cipient <scipient@example.com>" --stdout main..side >patch5 &&
 	sed -e "/^\$/q" patch5 >hdrs5 &&
 	grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
 	grep "^ *S E Cipient <scipient@example.com>\$" hdrs5
@@ -164,7 +167,7 @@
 
 test_expect_failure 'additional command line cc (rfc822)' '
 	git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-	git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+	git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout main..side >patch5 &&
 	sed -e "/^\$/q" patch5 >hdrs5 &&
 	grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
 	grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5
@@ -172,14 +175,14 @@
 
 test_expect_success 'command line headers' '
 	git config --unset-all format.headers &&
-	git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side >patch6 &&
+	git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout main..side >patch6 &&
 	sed -e "/^\$/q" patch6 >hdrs6 &&
 	grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6
 '
 
 test_expect_success 'configuration headers and command line headers' '
 	git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-	git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side >patch7 &&
+	git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout main..side >patch7 &&
 	sed -e "/^\$/q" patch7 >hdrs7 &&
 	grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 &&
 	grep "^ *S E Cipient <scipient@example.com>\$" hdrs7
@@ -187,40 +190,40 @@
 
 test_expect_success 'command line To: header (ascii)' '
 	git config --unset-all format.headers &&
-	git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+	git format-patch --to="R E Cipient <rcipient@example.com>" --stdout main..side >patch8 &&
 	sed -e "/^\$/q" patch8 >hdrs8 &&
 	grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc822)' '
-	git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+	git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout main..side >patch8 &&
 	sed -e "/^\$/q" patch8 >hdrs8 &&
 	grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc2047)' '
-	git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+	git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout main..side >patch8 &&
 	sed -e "/^\$/q" patch8 >hdrs8 &&
 	grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_success 'configuration To: header (ascii)' '
 	git config format.to "R E Cipient <rcipient@example.com>" &&
-	git format-patch --stdout master..side >patch9 &&
+	git format-patch --stdout main..side >patch9 &&
 	sed -e "/^\$/q" patch9 >hdrs9 &&
 	grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc822)' '
 	git config format.to "R. E. Cipient <rcipient@example.com>" &&
-	git format-patch --stdout master..side >patch9 &&
+	git format-patch --stdout main..side >patch9 &&
 	sed -e "/^\$/q" patch9 >hdrs9 &&
 	grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc2047)' '
 	git config format.to "R Ä Cipient <rcipient@example.com>" &&
-	git format-patch --stdout master..side >patch9 &&
+	git format-patch --stdout main..side >patch9 &&
 	sed -e "/^\$/q" patch9 >hdrs9 &&
 	grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9
 '
@@ -234,35 +237,35 @@
 }
 
 test_expect_success 'format.from=false' '
-	git -c format.from=false format-patch --stdout master..side >patch &&
+	git -c format.from=false format-patch --stdout main..side >patch &&
 	sed -e "/^\$/q" patch >hdrs &&
 	check_patch patch &&
 	! grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from=true' '
-	git -c format.from=true format-patch --stdout master..side >patch &&
+	git -c format.from=true format-patch --stdout main..side >patch &&
 	sed -e "/^\$/q" patch >hdrs &&
 	check_patch hdrs &&
 	grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from with address' '
-	git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side >patch &&
+	git -c format.from="F R Om <from@example.com>" format-patch --stdout main..side >patch &&
 	sed -e "/^\$/q" patch >hdrs &&
 	check_patch hdrs &&
 	grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--no-from overrides format.from' '
-	git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side >patch &&
+	git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout main..side >patch &&
 	sed -e "/^\$/q" patch >hdrs &&
 	check_patch hdrs &&
 	! grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--from overrides format.from' '
-	git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side >patch &&
+	git -c format.from="F R Om <from@example.com>" format-patch --from --stdout main..side >patch &&
 	sed -e "/^\$/q" patch >hdrs &&
 	check_patch hdrs &&
 	! grep "^From: F R Om <from@example.com>\$" hdrs
@@ -271,7 +274,7 @@
 test_expect_success '--no-to overrides config.to' '
 	git config --replace-all format.to \
 		"R E Cipient <rcipient@example.com>" &&
-	git format-patch --no-to --stdout master..side >patch10 &&
+	git format-patch --no-to --stdout main..side >patch10 &&
 	sed -e "/^\$/q" patch10 >hdrs10 &&
 	check_patch hdrs10 &&
 	! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10
@@ -281,7 +284,7 @@
 	git config --replace-all format.to \
 		"Someone <someone@out.there>" &&
 	git format-patch --no-to --to="Someone Else <else@out.there>" \
-		--stdout master..side >patch11 &&
+		--stdout main..side >patch11 &&
 	sed -e "/^\$/q" patch11 >hdrs11 &&
 	check_patch hdrs11 &&
 	! grep "^To: Someone <someone@out.there>\$" hdrs11 &&
@@ -291,7 +294,7 @@
 test_expect_success '--no-cc overrides config.cc' '
 	git config --replace-all format.cc \
 		"C E Cipient <rcipient@example.com>" &&
-	git format-patch --no-cc --stdout master..side >patch12 &&
+	git format-patch --no-cc --stdout main..side >patch12 &&
 	sed -e "/^\$/q" patch12 >hdrs12 &&
 	check_patch hdrs12 &&
 	! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12
@@ -300,7 +303,7 @@
 test_expect_success '--no-add-header overrides config.headers' '
 	git config --replace-all format.headers \
 		"Header1: B E Cipient <rcipient@example.com>" &&
-	git format-patch --no-add-header --stdout master..side >patch13 &&
+	git format-patch --no-add-header --stdout main..side >patch13 &&
 	sed -e "/^\$/q" patch13 >hdrs13 &&
 	check_patch hdrs13 &&
 	! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13
@@ -309,7 +312,7 @@
 test_expect_success 'multiple files' '
 	rm -rf patches/ &&
 	git checkout side &&
-	git format-patch -o patches/ master &&
+	git format-patch -o patches/ main &&
 	ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
 '
 
@@ -369,7 +372,7 @@
 
 test_expect_success 'reroll count' '
 	rm -fr patches &&
-	git format-patch -o patches --cover-letter --reroll-count 4 master..side >list &&
+	git format-patch -o patches --cover-letter --reroll-count 4 main..side >list &&
 	! grep -v "^patches/v4-000[0-3]-" list &&
 	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
@@ -377,7 +380,7 @@
 
 test_expect_success 'reroll count (-v)' '
 	rm -fr patches &&
-	git format-patch -o patches --cover-letter -v4 master..side >list &&
+	git format-patch -o patches --cover-letter -v4 main..side >list &&
 	! grep -v "^patches/v4-000[0-3]-" list &&
 	sed -n -e "/^Subject: /p" $(cat list) >subjects &&
 	! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects
@@ -413,7 +416,7 @@
 
 test_expect_success 'no threading' '
 	git checkout side &&
-	check_threading expect.no-threading master
+	check_threading expect.no-threading main
 '
 
 cat >expect.thread <<EOF
@@ -430,7 +433,7 @@
 EOF
 
 test_expect_success 'thread' '
-	check_threading expect.thread --thread master
+	check_threading expect.thread --thread main
 '
 
 cat >expect.in-reply-to <<EOF
@@ -450,7 +453,7 @@
 
 test_expect_success 'thread in-reply-to' '
 	check_threading expect.in-reply-to --in-reply-to="<test.message>" \
-		--thread master
+		--thread main
 '
 
 cat >expect.cover-letter <<EOF
@@ -471,7 +474,7 @@
 EOF
 
 test_expect_success 'thread cover-letter' '
-	check_threading expect.cover-letter --cover-letter --thread master
+	check_threading expect.cover-letter --cover-letter --thread main
 '
 
 cat >expect.cl-irt <<EOF
@@ -498,12 +501,12 @@
 
 test_expect_success 'thread cover-letter in-reply-to' '
 	check_threading expect.cl-irt --cover-letter \
-		--in-reply-to="<test.message>" --thread master
+		--in-reply-to="<test.message>" --thread main
 '
 
 test_expect_success 'thread explicit shallow' '
 	check_threading expect.cl-irt --cover-letter \
-		--in-reply-to="<test.message>" --thread=shallow master
+		--in-reply-to="<test.message>" --thread=shallow main
 '
 
 cat >expect.deep <<EOF
@@ -521,7 +524,7 @@
 EOF
 
 test_expect_success 'thread deep' '
-	check_threading expect.deep --thread=deep master
+	check_threading expect.deep --thread=deep main
 '
 
 cat >expect.deep-irt <<EOF
@@ -544,7 +547,7 @@
 
 test_expect_success 'thread deep in-reply-to' '
 	check_threading expect.deep-irt  --thread=deep \
-		--in-reply-to="<test.message>" master
+		--in-reply-to="<test.message>" main
 '
 
 cat >expect.deep-cl <<EOF
@@ -568,7 +571,7 @@
 EOF
 
 test_expect_success 'thread deep cover-letter' '
-	check_threading expect.deep-cl --cover-letter --thread=deep master
+	check_threading expect.deep-cl --cover-letter --thread=deep main
 '
 
 cat >expect.deep-cl-irt <<EOF
@@ -598,27 +601,27 @@
 
 test_expect_success 'thread deep cover-letter in-reply-to' '
 	check_threading expect.deep-cl-irt --cover-letter \
-		--in-reply-to="<test.message>" --thread=deep master
+		--in-reply-to="<test.message>" --thread=deep main
 '
 
 test_expect_success 'thread via config' '
 	test_config format.thread true &&
-	check_threading expect.thread master
+	check_threading expect.thread main
 '
 
 test_expect_success 'thread deep via config' '
 	test_config format.thread deep &&
-	check_threading expect.deep master
+	check_threading expect.deep main
 '
 
 test_expect_success 'thread config + override' '
 	test_config format.thread deep &&
-	check_threading expect.thread --thread master
+	check_threading expect.thread --thread main
 '
 
 test_expect_success 'thread config + --no-thread' '
 	test_config format.thread deep &&
-	check_threading expect.no-threading --no-thread master
+	check_threading expect.no-threading --no-thread main
 '
 
 test_expect_success 'excessive subject' '
@@ -631,7 +634,7 @@
 	after=$(git rev-parse --short $after) &&
 	git update-index file &&
 	git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
-	git format-patch -o patches/ master..side &&
+	git format-patch -o patches/ main..side &&
 	ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
 '
 
@@ -885,21 +888,21 @@
 
 test_expect_success 'options no longer allowed for format-patch' '
 	test_must_fail git format-patch --name-only 2>output &&
-	test_i18ncmp expect.name-only output &&
+	test_cmp expect.name-only output &&
 	test_must_fail git format-patch --name-status 2>output &&
-	test_i18ncmp expect.name-status output &&
+	test_cmp expect.name-status output &&
 	test_must_fail git format-patch --check 2>output &&
-	test_i18ncmp expect.check output
+	test_cmp expect.check output
 '
 
 test_expect_success 'format-patch --numstat should produce a patch' '
-	git format-patch --numstat --stdout master..side >output &&
+	git format-patch --numstat --stdout main..side >output &&
 	grep "^diff --git a/" output >diff &&
 	test_line_count = 5 diff
 '
 
 test_expect_success 'format-patch -- <path>' '
-	git format-patch master..side -- file 2>error &&
+	git format-patch main..side -- file 2>error &&
 	! grep "Use .--" error
 '
 
@@ -1673,9 +1676,9 @@
 	test_config branch.rebuild-1.description "config subject
 
 body" &&
-	test_must_fail git format-patch --cover-letter --cover-from-description garbage master &&
+	test_must_fail git format-patch --cover-letter --cover-from-description garbage main &&
 	test_config format.coverFromDescription garbage &&
-	test_must_fail git format-patch --cover-letter master
+	test_must_fail git format-patch --cover-letter main
 '
 
 test_expect_success 'cover letter with format.coverFromDescription = default' '
@@ -1684,7 +1687,7 @@
 body" &&
 	test_config format.coverFromDescription default &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^config subject$" actual &&
@@ -1696,7 +1699,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description default master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description default main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^config subject$" actual &&
@@ -1709,7 +1712,7 @@
 body" &&
 	test_config format.coverFromDescription none &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1721,7 +1724,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description none master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description none main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1734,7 +1737,7 @@
 body" &&
 	test_config format.coverFromDescription message &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^config subject$" actual &&
@@ -1746,7 +1749,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description message master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description message main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^config subject$" actual &&
@@ -1759,7 +1762,7 @@
 body" &&
 	test_config format.coverFromDescription subject &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1771,7 +1774,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description subject main >actual &&
 	grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1784,7 +1787,7 @@
 body" &&
 	test_config format.coverFromDescription auto &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1796,7 +1799,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description auto main >actual &&
 	grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1809,7 +1812,7 @@
 body" &&
 	test_config format.coverFromDescription auto &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
@@ -1821,7 +1824,7 @@
 
 body" &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description auto master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description auto main >actual &&
 	grep "^Subject: \[PATCH 0/2\] \*\*\* SUBJECT HERE \*\*\*$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	grep "^this is a really long first line and it is over 100 characters long which is the threshold for long subjects$" actual &&
@@ -1834,7 +1837,7 @@
 body" &&
 	test_config format.coverFromDescription none &&
 	git checkout rebuild-1 &&
-	git format-patch --stdout --cover-letter --cover-from-description subject master >actual &&
+	git format-patch --stdout --cover-letter --cover-from-description subject main >actual &&
 	grep "^Subject: \[PATCH 0/2\] config subject$" actual &&
 	! grep "^\*\*\* BLURB HERE \*\*\*$" actual &&
 	! grep "^config subject$" actual &&
@@ -1844,7 +1847,7 @@
 test_expect_success 'cover letter using branch description (1)' '
 	git checkout rebuild-1 &&
 	test_config branch.rebuild-1.description hello &&
-	git format-patch --stdout --cover-letter master >actual &&
+	git format-patch --stdout --cover-letter main >actual &&
 	grep hello actual
 '
 
@@ -1858,14 +1861,14 @@
 test_expect_success 'cover letter using branch description (3)' '
 	git checkout rebuild-1 &&
 	test_config branch.rebuild-1.description hello &&
-	git format-patch --stdout --cover-letter ^master rebuild-1 >actual &&
+	git format-patch --stdout --cover-letter ^main rebuild-1 >actual &&
 	grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (4)' '
 	git checkout rebuild-1 &&
 	test_config branch.rebuild-1.description hello &&
-	git format-patch --stdout --cover-letter master.. >actual &&
+	git format-patch --stdout --cover-letter main.. >actual &&
 	grep hello actual
 '
 
@@ -1932,8 +1935,8 @@
 
 test_expect_success 'format-patch -o with no leading directories' '
 	rm -fr patches &&
-	git format-patch -o patches master..side &&
-	count=$(git rev-list --count master..side) &&
+	git format-patch -o patches main..side &&
+	count=$(git rev-list --count main..side) &&
 	ls patches >list &&
 	test_line_count = $count list
 '
@@ -1941,16 +1944,16 @@
 test_expect_success 'format-patch -o with leading existing directories' '
 	rm -rf existing-dir &&
 	mkdir existing-dir &&
-	git format-patch -o existing-dir/patches master..side &&
-	count=$(git rev-list --count master..side) &&
+	git format-patch -o existing-dir/patches main..side &&
+	count=$(git rev-list --count main..side) &&
 	ls existing-dir/patches >list &&
 	test_line_count = $count list
 '
 
 test_expect_success 'format-patch -o with leading non-existing directories' '
 	rm -rf non-existing-dir &&
-	git format-patch -o non-existing-dir/patches master..side &&
-	count=$(git rev-list --count master..side) &&
+	git format-patch -o non-existing-dir/patches main..side &&
+	count=$(git rev-list --count main..side) &&
 	test_path_is_dir non-existing-dir &&
 	ls non-existing-dir/patches >list &&
 	test_line_count = $count list
@@ -1959,8 +1962,8 @@
 test_expect_success 'format-patch format.outputDirectory option' '
 	test_config format.outputDirectory patches &&
 	rm -fr patches &&
-	git format-patch master..side &&
-	count=$(git rev-list --count master..side) &&
+	git format-patch main..side &&
+	count=$(git rev-list --count main..side) &&
 	ls patches >list &&
 	test_line_count = $count list
 '
@@ -1968,7 +1971,7 @@
 test_expect_success 'format-patch -o overrides format.outputDirectory' '
 	test_config format.outputDirectory patches &&
 	rm -fr patches patchset &&
-	git format-patch master..side -o patchset &&
+	git format-patch main..side -o patchset &&
 	test_path_is_missing patches &&
 	test_path_is_dir patchset
 '
@@ -2065,14 +2068,14 @@
 	#	  ------------W
 	#
 	# If "format-patch Z..X" is given, P and Z can not be specified as the base commit
-	git checkout -b topic1 master &&
+	git checkout -b topic1 main &&
 	git rev-parse HEAD >commit-id-base &&
 	test_commit P &&
 	git rev-parse HEAD >commit-id-P &&
 	test_commit Z &&
 	git rev-parse HEAD >commit-id-Z &&
 	test_commit Y &&
-	git checkout -b topic2 master &&
+	git checkout -b topic2 main &&
 	test_commit W &&
 	git merge topic1 &&
 	test_commit X &&
@@ -2085,7 +2088,7 @@
 '
 
 test_expect_success 'format-patch --base=auto' '
-	git checkout -b upstream master &&
+	git checkout -b upstream main &&
 	git checkout -b local upstream &&
 	git branch --set-upstream-to=upstream &&
 	test_commit N1 &&
@@ -2106,11 +2109,11 @@
 	#  \ / \
 	#   C---M2---E
 	#
-	git checkout master &&
+	git checkout main &&
 	test_commit A &&
-	git checkout -b xb master &&
+	git checkout -b xb main &&
 	test_commit B &&
-	git checkout -b xc master &&
+	git checkout -b xc main &&
 	test_commit C &&
 	git checkout -b xbc xb -- &&
 	git merge xc &&
@@ -2230,7 +2233,7 @@
 '
 
 test_expect_success 'interdiff: setup' '
-	git checkout -b boop master &&
+	git checkout -b boop main &&
 	test_commit fnorp blorp &&
 	test_commit fleep blorp
 '
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 8c57422..2c13b62 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success "Ray Lehtiniemi's example" '
 	cat <<-\EOF >x &&
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
index 9c48e5c..876271d 100755
--- a/t/t4016-diff-quote.sh
+++ b/t/t4016-diff-quote.sh
@@ -82,7 +82,7 @@
 	 7 files changed, 0 insertions(+), 0 deletions(-)
 	EOF
 	git diff --stat -M HEAD >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
index 95a7ca7..ed461f4 100755
--- a/t/t4017-diff-retval.sh
+++ b/t/t4017-diff-retval.sh
@@ -2,6 +2,9 @@
 
 test_description='Return value of diffs'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -111,7 +114,7 @@
 	git checkout HEAD^ &&
 	echo binary >>b &&
 	git commit -m "side" b &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git add b &&
 	test_expect_code 2 git --no-pager diff --cached --check >test.out &&
 	test 3 = $(grep "conflict marker" test.out | wc -l) &&
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index d7145cc..94ef77e 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -3,7 +3,7 @@
 test_description='difference in submodules'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success setup '
 	test_tick &&
@@ -93,6 +93,14 @@
 	) &&
 	git diff HEAD >actual &&
 	sed -e "1,/^@@/d" actual >actual.body &&
+	expect_from_to >expect.body $subtip $subprev &&
+	test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked) (none ignored)' '
+	test_config diff.ignoreSubmodules none &&
+	git diff HEAD >actual &&
+	sed -e "1,/^@@/d" actual >actual.body &&
 	expect_from_to >expect.body $subtip $subprev-dirty &&
 	test_cmp expect.body actual.body
 '
@@ -168,13 +176,13 @@
 		git clean -qfdx &&
 		>cruft
 	) &&
-	git diff HEAD >actual &&
+	git diff --ignore-submodules=none HEAD >actual &&
 	sed -e "1,/^@@/d" actual >actual.body &&
 	expect_from_to >expect.body $subprev $subprev-dirty &&
 	test_cmp expect.body actual.body &&
 	git diff --ignore-submodules=all HEAD >actual2 &&
 	test_must_be_empty actual2 &&
-	git diff --ignore-submodules=untracked HEAD >actual3 &&
+	git diff HEAD >actual3 &&
 	test_must_be_empty actual3 &&
 	git diff --ignore-submodules=dirty HEAD >actual4 &&
 	test_must_be_empty actual4
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 4cb9f0e..c906320 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -139,7 +139,7 @@
 test_expect_success 'diffstat does not run textconv' '
 	echo file diff=fail >.gitattributes &&
 	git diff --stat HEAD^ HEAD >actual &&
-	test_i18ncmp expect.stat actual &&
+	test_cmp expect.stat actual &&
 
 	head -n1 <expect.stat >expect.line1 &&
 	head -n1 <actual >actual.line1 &&
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 0c8fb39..56f1e62 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -3,7 +3,7 @@
 test_description='word diff colors'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 cat >pre.simple <<-\EOF
 	h(4)
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
index 9468083..aeac203 100755
--- a/t/t4038-diff-combined.sh
+++ b/t/t4038-diff-combined.sh
@@ -2,8 +2,11 @@
 
 test_description='combined diff'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 setup_helper () {
 	one=$1 branch=$2 side=$3 &&
@@ -115,7 +118,7 @@
 '
 
 test_expect_success 'setup combined ignore spaces' '
-	git checkout master &&
+	git checkout main &&
 	>test &&
 	git add test &&
 	git commit -m initial &&
@@ -143,7 +146,7 @@
 	EOF
 	git commit -m "test other space changes" -a &&
 
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	tr -d Q <<-\EOF >test &&
 	eol spaces Q
 	space  change
@@ -404,7 +407,7 @@
 test_expect_success 'combine diff gets tree sorting right' '
 	# create a directory and a file that sort differently in trees
 	# versus byte-wise (implied "/" sorts after ".")
-	git checkout -f master &&
+	git checkout -f main &&
 	mkdir foo &&
 	echo base >foo/one &&
 	echo base >foo/two &&
@@ -414,9 +417,9 @@
 
 	# one side modifies a file in the directory, along with the root
 	# file...
-	echo master >foo/one &&
-	echo master >foo.ext &&
-	git commit -a -m master &&
+	echo main >foo/one &&
+	echo main >foo.ext &&
+	git commit -a -m main &&
 
 	# the other side modifies the other file in the directory
 	git checkout -b other HEAD^ &&
@@ -426,7 +429,7 @@
 	# And now we merge. The files in the subdirectory will resolve cleanly,
 	# meaning that a combined diff will not find them interesting. But it
 	# will find the tree itself interesting, because it had to be merged.
-	git checkout master &&
+	git checkout main &&
 	git merge other &&
 
 	printf "MM\tfoo\n" >expect &&
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
index f852136..0c1502d 100755
--- a/t/t4041-diff-submodule-option.sh
+++ b/t/t4041-diff-submodule-option.sh
@@ -9,6 +9,9 @@
 This test tries to verify the sanity of the --submodule option of git diff.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Tested non-UTF-8 encoding
@@ -262,7 +265,7 @@
 
 test_expect_success 'submodule contains untracked content' '
 	echo new > sm1/new-file &&
-	git diff-index -p --submodule=log HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	EOF
@@ -270,7 +273,7 @@
 '
 
 test_expect_success 'submodule contains untracked content (untracked ignored)' '
-	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	git diff-index -p --submodule=log HEAD >actual &&
 	test_must_be_empty actual
 '
 
@@ -286,7 +289,7 @@
 
 test_expect_success 'submodule contains untracked and modified content' '
 	echo new > sm1/foo6 &&
-	git diff-index -p --submodule=log HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 contains modified content
@@ -296,7 +299,7 @@
 
 test_expect_success 'submodule contains untracked and modified content (untracked ignored)' '
 	echo new > sm1/foo6 &&
-	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains modified content
 	EOF
@@ -337,7 +340,7 @@
 
 test_expect_success 'modified submodule contains untracked content' '
 	echo new > sm1/new-file &&
-	git diff-index -p --submodule=log HEAD >actual &&
+	git diff-index -p  --ignore-submodules=none --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 $head6..$head8:
@@ -347,7 +350,7 @@
 '
 
 test_expect_success 'modified submodule contains untracked content (untracked ignored)' '
-	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head6..$head8:
 	  > change
@@ -371,7 +374,7 @@
 
 test_expect_success 'modified submodule contains untracked and modified content' '
 	echo modification >> sm1/foo6 &&
-	git diff-index -p --submodule=log HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 contains modified content
@@ -383,7 +386,7 @@
 
 test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' '
 	echo modification >> sm1/foo6 &&
-	git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+	git diff-index -p --submodule=log HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains modified content
 	Submodule sm1 $head6..$head8:
@@ -538,7 +541,7 @@
 	(cd super &&
 		(cd sub &&
 			git fetch &&
-			git checkout origin/master
+			git checkout origin/main
 		) &&
 		git diff --submodule > ../actual
 	) &&
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index 7be1de7..61ba5f7 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -61,7 +61,7 @@
 	EOF
 	test_expect_success "--stat $*" "
 		git -C '$dir' diff --stat $* HEAD^ >actual &&
-		test_i18ncmp expected actual
+		test_cmp expected actual
 	"
 }
 
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
index 7f9ad9f..0260cf6 100755
--- a/t/t4048-diff-combined-binary.sh
+++ b/t/t4048-diff-combined-binary.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='combined and merge diff handle binary files and textconv'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup binary merge conflict' '
@@ -14,7 +17,7 @@
 	echo threeQ3 | q_to_nul >binary &&
 	git commit -a -m three &&
 	three=$(git rev-parse --short HEAD:binary) &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	echo resolvedQhooray | q_to_nul >binary &&
 	git commit -a -m resolved &&
 	res=$(git rev-parse --short HEAD:binary)
@@ -62,14 +65,14 @@
 '
 
 test_expect_success 'setup non-binary with binary attribute' '
-	git checkout master &&
+	git checkout main &&
 	test_commit one text &&
 	test_commit two text &&
 	two=$(git rev-parse --short HEAD:text) &&
 	git checkout -b branch-text HEAD^ &&
 	test_commit three text &&
 	three=$(git rev-parse --short HEAD:text) &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test_commit resolved text &&
 	res=$(git rev-parse --short HEAD:text) &&
 	echo text -diff >.gitattributes
@@ -206,11 +209,11 @@
  +THREE
 ++=======
 + TWO
-++>>>>>>> MASTER
+++>>>>>>> MAIN
 EOF
 test_expect_success 'diff --cc respects textconv on worktree file' '
 	git reset --hard HEAD^ &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git diff >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh
index a341217..53061b1 100755
--- a/t/t4049-diff-stat-count.sh
+++ b/t/t4049-diff-stat-count.sh
@@ -25,7 +25,7 @@
 	 4 files changed, 2 insertions(+)
 	EOF
 	git diff --stat --stat-count=2 HEAD >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'binary changes do not count in lines' '
@@ -40,7 +40,7 @@
 	 3 files changed, 2 insertions(+)
 	EOF
 	git diff --stat --stat-count=2 >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'exclude unmerged entries from total file count' '
@@ -62,7 +62,7 @@
 	 3 files changed, 3 insertions(+)
 	EOF
 	git diff --stat --stat-count=2 >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index 28c0538..9eba436 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -5,6 +5,9 @@
 
 test_description='test --stat output of various commands'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -353,7 +356,7 @@
 EOF
 test_expect_success 'merge --stat respects COLUMNS (big change)' '
 	git checkout -b branch HEAD^^ &&
-	COLUMNS=100 git merge --stat --no-ff master^ >output &&
+	COLUMNS=100 git merge --stat --no-ff main^ >output &&
 	grep " | " output >actual &&
 	test_cmp expect actual
 '
@@ -362,7 +365,7 @@
  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
 EOF
 test_expect_success 'merge --stat respects COLUMNS (long filename)' '
-	COLUMNS=100 git merge --stat --no-ff master >output &&
+	COLUMNS=100 git merge --stat --no-ff main >output &&
 	grep " | " output >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh
index 43dd474..aec1d9d 100755
--- a/t/t4056-diff-order.sh
+++ b/t/t4056-diff-order.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
-test_description='diff order'
+test_description='diff order & rotate'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
@@ -106,7 +109,7 @@
 test_expect_success 'setup for testing combine-diff order' '
 	git checkout -b tmp HEAD~ &&
 	create_files 3 &&
-	git checkout master &&
+	git checkout main &&
 	git merge --no-commit -s ours tmp &&
 	create_files 5
 '
@@ -124,4 +127,74 @@
 	'
 done
 
+### rotate and skip
+
+test_expect_success 'rotate and skip setup' '
+	>sample1.t &&
+	>sample2.t &&
+	>sample3.t &&
+	>sample4.t &&
+	git add sample[1234].t &&
+	git commit -m "added" sample[1234].t &&
+	echo modified >>sample1.t &&
+	echo modified >>sample2.t &&
+	echo modified >>sample4.t &&
+	git commit -m "updated" sample[1234].t
+'
+
+test_expect_success 'diff --rotate-to' '
+	git diff --rotate-to=sample2.t --name-only HEAD^ >actual &&
+	test_write_lines sample2.t sample4.t sample1.t >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'diff --skip-to' '
+	git diff --skip-to=sample2.t --name-only HEAD^ >actual &&
+	test_write_lines sample2.t sample4.t >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'diff --rotate/skip-to error condition' '
+	test_must_fail git diff --rotate-to=sample3.t HEAD^ &&
+	test_must_fail git diff --skip-to=sample3.t HEAD^
+'
+
+test_expect_success 'log --rotate-to' '
+	git log --rotate-to=sample3.t --raw HEAD~2.. >raw &&
+	# just distill the commit header and paths
+	sed -n -e "s/^commit.*/commit/p" \
+	       -e "/^:/s/^.*	//p" raw >actual &&
+
+	cat >expect <<-\EOF &&
+	commit
+	sample4.t
+	sample1.t
+	sample2.t
+	commit
+	sample3.t
+	sample4.t
+	sample1.t
+	sample2.t
+	EOF
+
+	test_cmp expect actual
+'
+
+test_expect_success 'log --skip-to' '
+	git log --skip-to=sample3.t --raw HEAD~2.. >raw &&
+	# just distill the commit header and paths
+	sed -n -e "s/^commit.*/commit/p" \
+	       -e "/^:/s/^.*	//p" raw >actual &&
+
+	cat >expect <<-\EOF &&
+	commit
+	sample4.t
+	commit
+	sample3.t
+	sample4.t
+	EOF
+
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4057-diff-combined-paths.sh b/t/t4057-diff-combined-paths.sh
index 0b78573..7e5b74f 100755
--- a/t/t4057-diff-combined-paths.sh
+++ b/t/t4057-diff-combined-paths.sh
@@ -2,6 +2,9 @@
 
 test_description='combined diff show only paths that are different to all parents'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # verify that diffc.expect matches output of
@@ -24,9 +27,9 @@
 		echo $i/2 >>$i.txt
 	done &&
 	git commit -a -m "side 2-9" &&
-	git checkout master &&
+	git checkout main &&
 	echo 1/2 >1.txt &&
-	git commit -a -m "master 1" &&
+	git commit -a -m "main 1" &&
 	git merge side &&
 	>diffc.expect &&
 	diffc_verify
@@ -41,19 +44,19 @@
 	done &&
 	echo "4side" >>4.txt &&
 	git commit -a -m "side 2-9 +4" &&
-	git checkout master &&
+	git checkout main &&
 	for i in $(test_seq 1 9)
 	do
 		echo $i/3 >>$i.txt
 	done &&
-	echo "4master" >>4.txt &&
-	git commit -a -m "master 1-9 +4" &&
+	echo "4main" >>4.txt &&
+	git commit -a -m "main 1-9 +4" &&
 	test_must_fail git merge side &&
 	cat <<-\EOF >4.txt &&
 	4
 	4/2
 	4/3
-	4master
+	4main
 	4side
 	EOF
 	git add 4.txt &&
@@ -69,12 +72,12 @@
 		echo $i/4 >>$i.txt
 	done &&
 	git commit -a -m "side 5-9" &&
-	git checkout master &&
+	git checkout main &&
 	for i in $(test_seq 1 3)
 	do
 		echo $i/4 >>$i.txt
 	done &&
-	git commit -a -m "master 1-3 +4hello" &&
+	git commit -a -m "main 1-3 +4hello" &&
 	git merge side &&
 	echo "Hello World" >4hello.txt &&
 	git add 4hello.txt &&
@@ -90,12 +93,12 @@
 		echo $i/5 >>$i.txt
 	done &&
 	git commit -a -m "side 5-9" &&
-	git checkout master &&
+	git checkout main &&
 	for i in $(test_seq 1 3)
 	do
 		echo $i/4 >>$i.txt
 	done &&
-	git commit -a -m "master 1-3" &&
+	git commit -a -m "main 1-3" &&
 	git merge side &&
 	git rm 4.txt &&
 	git commit --amend &&
diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh
index c24ee17..54614b8 100755
--- a/t/t4058-diff-duplicates.sh
+++ b/t/t4058-diff-duplicates.sh
@@ -1,5 +1,14 @@
 #!/bin/sh
 
+# NOTICE:
+#   This testsuite does a number of diffs and checks that the output match.
+#   However, it is a "garbage in, garbage out" situation; the trees have
+#   duplicate entries for individual paths, and it results in diffs that do
+#   not make much sense.  As such, it is not clear that the diffs are
+#   "correct".  The primary purpose of these tests was to verify that
+#   diff-tree does not segfault, but there is perhaps some value in ensuring
+#   that the diff output isn't wildly unreasonable.
+
 test_description='test tree diff when trees have duplicate entries'
 . ./test-lib.sh
 
@@ -57,7 +66,16 @@
 	git tag two $outer_two
 '
 
-test_expect_success 'diff-tree between trees' '
+test_expect_success 'create tree without duplicate entries' '
+	blob_one=$(echo one | git hash-object -w --stdin) &&
+	outer_three=$(make_tree \
+		100644 renamed $blob_one
+	) &&
+	git tag three $outer_three
+'
+
+test_expect_success 'diff-tree between duplicate trees' '
+	# See NOTICE at top of file
 	{
 		printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" &&
 		printf ":000000 100644 $ZERO_OID $blob_two A\touter/inner\n" &&
@@ -71,9 +89,101 @@
 '
 
 test_expect_success 'diff-tree with renames' '
-	# same expectation as above, since we disable rename detection
+	# See NOTICE at top of file.
 	git diff-tree -M -r --no-abbrev one two >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'diff-tree FROM duplicate tree' '
+	# See NOTICE at top of file.
+	{
+		printf ":100644 000000 $blob_one $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":000000 100644 $ZERO_OID $blob_one A\trenamed\n"
+	} >expect &&
+	git diff-tree -r --no-abbrev one three >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'diff-tree FROM duplicate tree, with renames' '
+	# See NOTICE at top of file.
+	{
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 000000 $blob_two $ZERO_OID D\touter/inner\n" &&
+		printf ":100644 100644 $blob_one $blob_one R100\touter/inner\trenamed\n"
+	} >expect &&
+	git diff-tree -M -r --no-abbrev one three >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create a few commits' '
+	git commit-tree -m "Duplicate Entries" two^{tree} >commit_id &&
+	git branch base $(cat commit_id) &&
+
+	git commit-tree -p $(cat commit_id) -m "Just one" three^{tree} >up &&
+	git branch update $(cat up) &&
+
+	git commit-tree -p $(cat up) -m "Back to weird" two^{tree} >final &&
+	git branch final $(cat final) &&
+
+	rm commit_id up final
+'
+
+test_expect_failure 'git read-tree does not segfault' '
+	test_when_finished rm .git/index.lock &&
+	test_might_fail git read-tree --reset base
+'
+
+test_expect_failure 'reset --hard does not segfault' '
+	test_when_finished rm .git/index.lock &&
+	git checkout base &&
+	test_might_fail git reset --hard
+'
+
+test_expect_failure 'git diff HEAD does not segfault' '
+	git checkout base &&
+	GIT_TEST_CHECK_CACHE_TREE=false &&
+	git reset --hard &&
+	test_might_fail git diff HEAD
+'
+
+test_expect_failure 'can switch to another branch when status is empty' '
+	git clean -ffdqx &&
+	git status --porcelain -uno >actual &&
+	test_must_be_empty actual &&
+	git checkout update
+'
+
+test_expect_success 'forcibly switch to another branch, verify status empty' '
+	git checkout -f update &&
+	git status --porcelain -uno >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'fast-forward from non-duplicate entries to duplicate' '
+	git merge final
+'
+
+test_expect_failure 'clean status, switch branches, status still clean' '
+	git status --porcelain -uno >actual &&
+	test_must_be_empty actual &&
+	git checkout base &&
+	git status --porcelain -uno >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success 'switch to base branch and force status to be clean' '
+	git checkout base &&
+	GIT_TEST_CHECK_CACHE_TREE=false git reset --hard &&
+	git status --porcelain -uno >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_failure 'fast-forward from duplicate entries to non-duplicate' '
+	git merge update
+'
+
 test_done
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index fc8229c..dc7b242 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -409,7 +409,7 @@
 
 test_expect_success 'submodule contains untracked content' '
 	echo new > sm1/new-file &&
-	git diff-index -p --submodule=diff HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	EOF
@@ -417,7 +417,7 @@
 '
 
 test_expect_success 'submodule contains untracked content (untracked ignored)' '
-	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	git diff-index -p --submodule=diff HEAD >actual &&
 	test_must_be_empty actual
 '
 
@@ -433,7 +433,7 @@
 
 test_expect_success 'submodule contains untracked and modified content' '
 	echo new > sm1/foo6 &&
-	git diff-index -p --submodule=diff HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 contains modified content
@@ -451,7 +451,7 @@
 # NOT OK
 test_expect_success 'submodule contains untracked and modified content (untracked ignored)' '
 	echo new > sm1/foo6 &&
-	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	git diff-index -p --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains modified content
 	diff --git a/sm1/foo6 b/sm1/foo6
@@ -512,7 +512,7 @@
 
 test_expect_success 'modified submodule contains untracked content' '
 	echo new > sm1/new-file &&
-	git diff-index -p --submodule=diff HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 $head7..$head8:
@@ -528,7 +528,7 @@
 '
 
 test_expect_success 'modified submodule contains untracked content (untracked ignored)' '
-	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	git diff-index -p --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 $head7..$head8:
 	diff --git a/sm1/foo6 b/sm1/foo6
@@ -564,7 +564,7 @@
 
 test_expect_success 'modified submodule contains untracked and modified content' '
 	echo modification >> sm1/foo6 &&
-	git diff-index -p --submodule=diff HEAD >actual &&
+	git diff-index -p --ignore-submodules=none --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains untracked content
 	Submodule sm1 contains modified content
@@ -583,7 +583,7 @@
 
 test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' '
 	echo modification >> sm1/foo6 &&
-	git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
+	git diff-index -p --submodule=diff HEAD >actual &&
 	cat >expected <<-EOF &&
 	Submodule sm1 contains modified content
 	Submodule sm1 $head7..$head8:
diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh
index 0f7a6d9..7750b87 100755
--- a/t/t4061-diff-indent.sh
+++ b/t/t4061-diff-indent.sh
@@ -3,8 +3,11 @@
 test_description='Test diff indent heuristic.
 
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 # Compare two diff outputs. Ignore "index" lines, because we don't
 # care about SHA-1s or file modes.
@@ -266,7 +269,7 @@
 	git reset --soft HEAD~ &&
 	git diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted &&
 	compare_diff spaces-compacted-expect out-diff-index-compacted &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-index: nice spaces with diff.indentHeuristic=true' '
@@ -274,7 +277,7 @@
 	git reset --soft HEAD~ &&
 	git -c diff.indentHeuristic=true diff-index -p old -- spaces.txt >out-diff-index-compacted2 &&
 	compare_diff spaces-compacted-expect out-diff-index-compacted2 &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-index: ugly spaces with --no-indent-heuristic' '
@@ -282,7 +285,7 @@
 	git reset --soft HEAD~ &&
 	git diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index &&
 	compare_diff spaces-expect out-diff-index &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-index: ugly spaces with diff.indentHeuristic=false' '
@@ -290,7 +293,7 @@
 	git reset --soft HEAD~ &&
 	git -c diff.indentHeuristic=false diff-index -p old -- spaces.txt >out-diff-index2 &&
 	compare_diff spaces-expect out-diff-index2 &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-index: --indent-heuristic overrides config' '
@@ -298,7 +301,7 @@
 	git reset --soft HEAD~ &&
 	git -c diff.indentHeuristic=false diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted3 &&
 	compare_diff spaces-compacted-expect out-diff-index-compacted3 &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-index: --no-indent-heuristic overrides config' '
@@ -306,7 +309,7 @@
 	git reset --soft HEAD~ &&
 	git -c diff.indentHeuristic=true diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index3 &&
 	compare_diff spaces-expect out-diff-index3 &&
-	git checkout -f master
+	git checkout -f main
 '
 
 # --- diff-files tests ----------------------------------------------------
@@ -317,7 +320,7 @@
 	git diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw &&
 	grep -v index out-diff-files-raw >out-diff-files-compacted &&
 	compare_diff spaces-compacted-expect out-diff-files-compacted &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-files: nice spaces with diff.indentHeuristic=true' '
@@ -326,7 +329,7 @@
 	git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw2 &&
 	grep -v index out-diff-files-raw2 >out-diff-files-compacted2 &&
 	compare_diff spaces-compacted-expect out-diff-files-compacted2 &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-files: ugly spaces with --no-indent-heuristic' '
@@ -335,7 +338,7 @@
 	git diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw &&
 	grep -v index out-diff-files-raw >out-diff-files &&
 	compare_diff spaces-expect out-diff-files &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-files: ugly spaces with diff.indentHeuristic=false' '
@@ -344,7 +347,7 @@
 	git -c diff.indentHeuristic=false diff-files -p spaces.txt >out-diff-files-raw2 &&
 	grep -v index out-diff-files-raw2 >out-diff-files &&
 	compare_diff spaces-expect out-diff-files &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-files: --indent-heuristic overrides config' '
@@ -353,7 +356,7 @@
 	git -c diff.indentHeuristic=false diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw3 &&
 	grep -v index out-diff-files-raw3 >out-diff-files-compacted &&
 	compare_diff spaces-compacted-expect out-diff-files-compacted &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_expect_success 'diff-files: --no-indent-heuristic overrides config' '
@@ -362,7 +365,7 @@
 	git -c diff.indentHeuristic=true diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw4 &&
 	grep -v index out-diff-files-raw4 >out-diff-files &&
 	compare_diff spaces-expect out-diff-files &&
-	git checkout -f master
+	git checkout -f main
 '
 
 test_done
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index 6331f63..a1de63b 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test combined/stat/moved interaction'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
@@ -13,7 +16,7 @@
 	test_commit B &&
 	git checkout -b side HEAD^ &&
 	test_commit C &&
-	git merge -m M master &&
+	git merge -m M main &&
 	test_commit D
 '
 
diff --git a/t/t4068-diff-symmetric-merge-base.sh b/t/t4068-diff-symmetric-merge-base.sh
index 03487cc..2d650d8 100755
--- a/t/t4068-diff-symmetric-merge-base.sh
+++ b/t/t4068-diff-symmetric-merge-base.sh
@@ -2,14 +2,17 @@
 
 test_description='behavior of diff with symmetric-diff setups and --merge-base'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # build these situations:
 #  - normal merge with one merge base (br1...b2r);
-#  - criss-cross merge ie 2 merge bases (br1...master);
-#  - disjoint subgraph (orphan branch, br3...master).
+#  - criss-cross merge ie 2 merge bases (br1...main);
+#  - disjoint subgraph (orphan branch, br3...main).
 #
-#     B---E   <-- master
+#     B---E   <-- main
 #    / \ /
 #   A   X
 #    \ / \
@@ -32,9 +35,9 @@
 	git add c &&
 	git commit -m C &&
 	git tag commit-C &&
-	git merge -m D master &&
+	git merge -m D main &&
 	git tag commit-D &&
-	git checkout master &&
+	git checkout main &&
 	git merge -m E commit-C &&
 	git checkout -b br2 commit-C &&
 	echo f >f &&
@@ -58,7 +61,7 @@
 # It should have one of those two, which comes out
 # to seven lines.
 test_expect_success 'diff with two merge bases' '
-	git diff br1...master >tmp 2>err &&
+	git diff br1...main >tmp 2>err &&
 	test_line_count = 7 tmp &&
 	test_line_count = 1 err
 '
@@ -69,22 +72,22 @@
 '
 
 test_expect_success 'diff with too many symmetric differences' '
-	test_must_fail git diff br1...master br2...br3 2>err &&
+	test_must_fail git diff br1...main br2...br3 2>err &&
 	test_i18ngrep "usage" err
 '
 
 test_expect_success 'diff with symmetric difference and extraneous arg' '
-	test_must_fail git diff master br1...master 2>err &&
+	test_must_fail git diff main br1...main 2>err &&
 	test_i18ngrep "usage" err
 '
 
 test_expect_success 'diff with two ranges' '
-	test_must_fail git diff master br1..master br2..br3 2>err &&
+	test_must_fail git diff main br1..main br2..br3 2>err &&
 	test_i18ngrep "usage" err
 '
 
 test_expect_success 'diff with ranges and extra arg' '
-	test_must_fail git diff master br1..master commit-D 2>err &&
+	test_must_fail git diff main br1..main commit-D 2>err &&
 	test_i18ngrep "usage" err
 '
 
@@ -93,21 +96,21 @@
 '
 
 test_expect_success 'diff --merge-base with three commits' '
-	test_must_fail git diff --merge-base br1 br2 master 2>err &&
+	test_must_fail git diff --merge-base br1 br2 main 2>err &&
 	test_i18ngrep "usage" err
 '
 
 for cmd in diff-index diff
 do
 	test_expect_success "$cmd --merge-base with one commit" '
-		git checkout master &&
+		git checkout main &&
 		git $cmd commit-C >expect &&
 		git $cmd --merge-base br2 >actual &&
 		test_cmp expect actual
 	'
 
 	test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
-		git checkout master &&
+		git checkout main &&
 		test_when_finished git reset --hard &&
 		echo unstaged >>c &&
 		git $cmd commit-C >expect &&
@@ -116,7 +119,7 @@
 	'
 
 	test_expect_success "$cmd --merge-base with one commit and staged and unstaged changes" '
-		git checkout master &&
+		git checkout main &&
 		test_when_finished git reset --hard &&
 		echo staged >>c &&
 		git add c &&
@@ -127,7 +130,7 @@
 	'
 
 	test_expect_success "$cmd --merge-base --cached with one commit and staged and unstaged changes" '
-		git checkout master &&
+		git checkout main &&
 		test_when_finished git reset --hard &&
 		echo staged >>c &&
 		git add c &&
@@ -138,19 +141,19 @@
 	'
 
 	test_expect_success "$cmd --merge-base with non-commit" '
-		git checkout master &&
-		test_must_fail git $cmd --merge-base master^{tree} 2>err &&
+		git checkout main &&
+		test_must_fail git $cmd --merge-base main^{tree} 2>err &&
 		test_i18ngrep "fatal: --merge-base only works with commits" err
 	'
 
 	test_expect_success "$cmd --merge-base with no merge bases and one commit" '
-		git checkout master &&
+		git checkout main &&
 		test_must_fail git $cmd --merge-base br3 2>err &&
 		test_i18ngrep "fatal: no merge base found" err
 	'
 
 	test_expect_success "$cmd --merge-base with multiple merge bases and one commit" '
-		git checkout master &&
+		git checkout main &&
 		test_must_fail git $cmd --merge-base br1 2>err &&
 		test_i18ngrep "fatal: multiple merge bases found" err
 	'
@@ -159,13 +162,13 @@
 for cmd in diff-tree diff
 do
 	test_expect_success "$cmd --merge-base with two commits" '
-		git $cmd commit-C master >expect &&
-		git $cmd --merge-base br2 master >actual &&
+		git $cmd commit-C main >expect &&
+		git $cmd --merge-base br2 main >actual &&
 		test_cmp expect actual
 	'
 
 	test_expect_success "$cmd --merge-base commit and non-commit" '
-		test_must_fail git $cmd --merge-base br2 master^{tree} 2>err &&
+		test_must_fail git $cmd --merge-base br2 main^{tree} 2>err &&
 		test_i18ngrep "fatal: --merge-base only works with commits" err
 	'
 
@@ -175,13 +178,13 @@
 	'
 
 	test_expect_success "$cmd --merge-base with multiple merge bases and two commits" '
-		test_must_fail git $cmd --merge-base master br1 2>err &&
+		test_must_fail git $cmd --merge-base main br1 2>err &&
 		test_i18ngrep "fatal: multiple merge bases found" err
 	'
 done
 
 test_expect_success 'diff-tree --merge-base with one commit' '
-	test_must_fail git diff-tree --merge-base master 2>err &&
+	test_must_fail git diff-tree --merge-base main 2>err &&
 	test_i18ngrep "fatal: --merge-base only works with two commits" err
 '
 
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index 744b8e5..9b433de 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -17,13 +17,13 @@
 	test_expect_success "$title" '
 		git apply --stat --summary \
 			<"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-		test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 
 	test_expect_success "$title with recount" '
 		sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
 		git apply --recount --stat --summary >current &&
-		test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 done <<\EOF
 rename
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 1b420e3..fad6d3f 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -6,6 +6,9 @@
 test_description='git apply handling binary patches
 
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -31,64 +34,64 @@
 	git update-index --add --remove file1 file2 file3 file4 &&
 	git commit -m "Second Version" &&
 
-	git diff-tree -p master binary >B.diff &&
-	git diff-tree -p -C master binary >C.diff &&
+	git diff-tree -p main binary >B.diff &&
+	git diff-tree -p -C main binary >C.diff &&
 
-	git diff-tree -p --binary master binary >BF.diff &&
-	git diff-tree -p --binary -C master binary >CF.diff &&
+	git diff-tree -p --binary main binary >BF.diff &&
+	git diff-tree -p --binary -C main binary >CF.diff &&
 
-	git diff-tree -p --full-index master binary >B-index.diff &&
-	git diff-tree -p -C --full-index master binary >C-index.diff &&
+	git diff-tree -p --full-index main binary >B-index.diff &&
+	git diff-tree -p -C --full-index main binary >C-index.diff &&
 
-	git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff &&
+	git diff-tree -p --binary --no-prefix main binary -- file3 >B0.diff &&
 
 	git init other-repo &&
 	(
 		cd other-repo &&
-		git fetch .. master &&
+		git fetch .. main &&
 		git reset --hard FETCH_HEAD
 	)
 '
 
 test_expect_success 'stat binary diff -- should not fail.' \
-	'git checkout master &&
+	'git checkout main &&
 	 git apply --stat --summary B.diff'
 
 test_expect_success 'stat binary -p0 diff -- should not fail.' '
-	 git checkout master &&
+	 git checkout main &&
 	 git apply --stat -p0 B0.diff
 '
 
 test_expect_success 'stat binary diff (copy) -- should not fail.' \
-	'git checkout master &&
+	'git checkout main &&
 	 git apply --stat --summary C.diff'
 
 test_expect_success 'check binary diff -- should fail.' \
-	'git checkout master &&
+	'git checkout main &&
 	 test_must_fail git apply --check B.diff'
 
 test_expect_success 'check binary diff (copy) -- should fail.' \
-	'git checkout master &&
+	'git checkout main &&
 	 test_must_fail git apply --check C.diff'
 
 test_expect_success \
 	'check incomplete binary diff with replacement -- should fail.' '
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git apply --check --allow-binary-replacement B.diff
 '
 
 test_expect_success \
     'check incomplete binary diff with replacement (copy) -- should fail.' '
-	 git checkout master &&
+	 git checkout main &&
 	 test_must_fail git apply --check --allow-binary-replacement C.diff
 '
 
 test_expect_success 'check binary diff with replacement.' \
-	'git checkout master &&
+	'git checkout main &&
 	 git apply --check --allow-binary-replacement BF.diff'
 
 test_expect_success 'check binary diff with replacement (copy).' \
-	'git checkout master &&
+	'git checkout main &&
 	 git apply --check --allow-binary-replacement CF.diff'
 
 # Now we start applying them.
@@ -96,7 +99,7 @@
 do_reset () {
 	rm -f file? &&
 	git reset --hard &&
-	git checkout -f master
+	git checkout -f main
 }
 
 test_expect_success 'apply binary diff -- should fail.' \
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
index d7349ce..d62db3f 100755
--- a/t/t4108-apply-threeway.sh
+++ b/t/t4108-apply-threeway.sh
@@ -2,6 +2,9 @@
 
 test_description='git apply --3way'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 print_sanitized_conflicted_diff () {
@@ -24,14 +27,14 @@
 	test_tick &&
 	test_write_lines 1 two 3 4 5 six 7 >one &&
 	test_write_lines 1 two 3 4 5 6 7 >two &&
-	git commit -a -m master &&
+	git commit -a -m main &&
 
 	git checkout side &&
 	test_write_lines 1 2 3 4 five 6 7 >one &&
 	test_write_lines 1 2 3 4 five 6 7 >two &&
 	git commit -a -m side &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'apply without --3way' '
@@ -39,7 +42,7 @@
 
 	# should fail to apply
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git apply --index P.diff &&
 	# should leave things intact
 	git diff-files --exit-code &&
@@ -52,14 +55,14 @@
 
 	# The corresponding conflicted merge
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git merge --no-commit side &&
 	git ls-files -s >expect.ls &&
 	print_sanitized_conflicted_diff >expect.diff &&
 
 	# should fail to apply
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git apply --index --3way P.diff &&
 	git ls-files -s >actual.ls &&
 	print_sanitized_conflicted_diff >actual.diff &&
@@ -86,7 +89,7 @@
 
 	# The corresponding conflicted merge
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git merge --no-commit side &&
 
 	# Manually resolve and record the resolution
@@ -96,7 +99,7 @@
 
 	# should fail to apply
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git apply --index --3way P.diff &&
 
 	# but rerere should have replayed the recorded resolution
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
index 66368ef..b45454a 100755
--- a/t/t4121-apply-diffs.sh
+++ b/t/t4121-apply-diffs.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git apply for contextually independent diffs'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 echo '1
@@ -23,7 +26,7 @@
 	git commit -a -q -m 2 &&
 	echo 9 >>file &&
 	git commit -a -q -m 3 &&
-	git checkout master'
+	git checkout main'
 
 test_expect_success \
 	'check if contextually independent diffs for the same file apply' \
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index 4acb3f3..aa52de4 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='apply to deeper directory without getting fooled with symlink'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -35,11 +38,11 @@
 
 test_expect_success 'check result' '
 
-	git diff --exit-code master &&
-	git diff --exit-code --cached master &&
+	git diff --exit-code main &&
+	git diff --exit-code --cached main &&
 	test_tick &&
 	git commit -m replay &&
-	T1=$(git rev-parse "master^{tree}") &&
+	T1=$(git rev-parse "main^{tree}") &&
 	T2=$(git rev-parse "HEAD^{tree}") &&
 	test "z$T1" = "z$T2"
 
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 3ecbef6..2aaaa0d 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -2,6 +2,9 @@
 
 test_description='git am running'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup: messages' '
@@ -179,8 +182,8 @@
 	test_tick &&
 	git commit -m "added another file" &&
 
-	git format-patch --stdout master >lorem-move.patch &&
-	git format-patch --no-prefix --stdout master >lorem-zero.patch &&
+	git format-patch --stdout main >lorem-move.patch &&
+	git format-patch --no-prefix --stdout main >lorem-zero.patch &&
 
 	git checkout -b rename &&
 	git mv file renamed &&
@@ -453,11 +456,11 @@
 	git checkout first &&
 	git am patch2 &&
 	test_path_is_missing .git/rebase-apply &&
-	test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
-	git diff --exit-code master..HEAD &&
-	git diff --exit-code master^..HEAD^ &&
-	compare author master HEAD &&
-	compare author master^ HEAD^ &&
+	test "$(git rev-parse main^^)" = "$(git rev-parse HEAD^^)" &&
+	git diff --exit-code main..HEAD &&
+	git diff --exit-code main^..HEAD^ &&
+	compare author main HEAD &&
+	compare author main^ HEAD^ &&
 	test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
 	     "$(git log -1 --pretty=format:"%cn <%ce>" HEAD)"
 '
@@ -759,7 +762,7 @@
 	git checkout first &&
 	cat pine patch1 | git am &&
 	test_path_is_missing .git/rebase-apply &&
-	git diff --exit-code master^..HEAD
+	git diff --exit-code main^..HEAD
 '
 
 test_expect_success 'am fails on mail without patch' '
@@ -903,7 +906,7 @@
 	test_tick &&
 	test_must_fail git am empty-file 2>actual &&
 	echo Patch format detection failed. >expected &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'am --message-id really adds the message id' '
@@ -1112,21 +1115,21 @@
 		test_commit sixth &&
 
 		git checkout test &&
-		git format-patch --stdout master..HEAD >patches &&
-		git reset --hard master &&
+		git format-patch --stdout main..HEAD >patches &&
+		git reset --hard main &&
 		git am patches &&
 		grep "smudged" a.txt &&
 
 		git checkout removal &&
 		git reset --hard &&
-		git format-patch --stdout master..HEAD >patches &&
-		git reset --hard master &&
+		git format-patch --stdout main..HEAD >patches &&
+		git reset --hard main &&
 		git am patches &&
 		grep "clean" a.txt &&
 
 		git checkout conflict &&
 		git reset --hard &&
-		git format-patch --stdout master..HEAD >patches &&
+		git format-patch --stdout main..HEAD >patches &&
 		git reset --hard fourth &&
 		test_must_fail git am -3 patches &&
 		grep "<<<<<<<<<<" a.txt
diff --git a/t/t4153-am-resume-override-opts.sh b/t/t4153-am-resume-override-opts.sh
index 8ea22d1..b7c3861 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -61,7 +61,7 @@
 	# Applying side2 will be quiet.
 	git am --no-quiet --continue >out &&
 	echo "Applying: side1" >expected &&
-	test_i18ncmp expected out
+	test_cmp expected out
 '
 
 test_expect_success '--signoff overrides --no-signoff' '
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index b12b43e..9f8c76d 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -8,7 +8,7 @@
 ! [fifth] version1
  ! [first] first
   ! [fourth] version1
-   ! [master] initial
+   ! [main] initial
     ! [second] prefer first over second
      ! [third] version2
 ------
@@ -19,9 +19,12 @@
     -  [second] prefer first over second
  +  +  [first] first
     +  [second^] second
-++++++ [master] initial
+++++++ [main] initial
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -57,7 +60,7 @@
 	test_tick &&
 	git commit -q -a -m first &&
 
-	git checkout -b second master &&
+	git checkout -b second main &&
 	git show first:a1 |
 	sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 &&
 	echo "* END *" >>a1 &&
@@ -168,7 +171,7 @@
 
 	oldmtimepost=$(test-tool chmtime --get -60 $rr/postimage) &&
 
-	git checkout -b third master &&
+	git checkout -b third main &&
 	git show second^:a1 | sed "s/To die: t/To die! T/" >a1 &&
 	git commit -q -a -m third &&
 
@@ -580,13 +583,13 @@
 test_expect_success 'rerere with unexpected conflict markers does not crash' '
 	git reset --hard &&
 
-	git checkout -b branch-1 master &&
+	git checkout -b branch-1 main &&
 	echo "bar" >test &&
 	git add test &&
 	git commit -q -m two &&
 
 	git reset --hard &&
-	git checkout -b branch-2 master &&
+	git checkout -b branch-2 main &&
 	echo "foo" >test &&
 	git add test &&
 	git commit -q -a -m one &&
@@ -601,7 +604,7 @@
 test_expect_success 'rerere with inner conflict markers' '
 	git reset --hard &&
 
-	git checkout -b A master &&
+	git checkout -b A main &&
 	echo "bar" >test &&
 	git add test &&
 	git commit -q -m two &&
@@ -610,7 +613,7 @@
 	git commit -q -m three &&
 
 	git reset --hard &&
-	git checkout -b B master &&
+	git checkout -b B main &&
 	echo "foo" >test &&
 	git add test &&
 	git commit -q -a -m one &&
@@ -651,11 +654,11 @@
 		git add original &&
 		git commit -m original &&
 
-		git checkout -b A master &&
+		git checkout -b A main &&
 		git mv original A &&
 		git commit -m "rename to A" &&
 
-		git checkout -b B master &&
+		git checkout -b B main &&
 		git mv original B &&
 		git commit -m "rename to B"
 	)
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
index 3d5c4a2..3095b1b 100755
--- a/t/t4201-shortlog.sh
+++ b/t/t4201-shortlog.sh
@@ -6,6 +6,9 @@
 test_description='git shortlog
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -191,7 +194,7 @@
 '
 
 test_expect_success 'shortlog with --output=<file>' '
-	git shortlog --output=shortlog -1 master >output &&
+	git shortlog --output=shortlog -1 main >output &&
 	test_must_be_empty output &&
 	test_line_count = 3 shortlog
 '
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 56d34ed..350cfa3 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -2,6 +2,9 @@
 
 test_description='git log'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 . "$TEST_DIRECTORY/lib-terminal.sh"
@@ -478,7 +481,7 @@
 	git checkout -b side HEAD~4 &&
 	test_commit side-1 1 1 &&
 	test_commit side-2 2 2 &&
-	git checkout master &&
+	git checkout main &&
 	git merge side
 '
 
@@ -541,17 +544,17 @@
 '
 
 test_expect_success 'log --raw --graph -m with merge' '
-	git log --raw --graph --oneline -m master | head -n 500 >actual &&
+	git log --raw --graph --oneline -m main | head -n 500 >actual &&
 	grep "initial" actual
 '
 
 test_expect_success 'diff-tree --graph' '
-	git diff-tree --graph master^ | head -n 500 >actual &&
+	git diff-tree --graph main^ | head -n 500 >actual &&
 	grep "one" actual
 '
 
 cat > expect <<\EOF
-*   commit master
+*   commit main
 |\  Merge: A B
 | | Author: A U Thor <author@example.com>
 | |
@@ -567,22 +570,22 @@
 | |
 | |     side-1
 | |
-* | commit master~1
+* | commit main~1
 | | Author: A U Thor <author@example.com>
 | |
 | |     Second
 | |
-* | commit master~2
+* | commit main~2
 | | Author: A U Thor <author@example.com>
 | |
 | |     sixth
 | |
-* | commit master~3
+* | commit main~3
 | | Author: A U Thor <author@example.com>
 | |
 | |     fifth
 | |
-* | commit master~4
+* | commit main~4
 |/  Author: A U Thor <author@example.com>
 |
 |       fourth
@@ -613,19 +616,19 @@
 test_expect_success 'set up more tangled history' '
 	git checkout -b tangle HEAD~6 &&
 	test_commit tangle-a tangle-a a &&
-	git merge master~3 &&
+	git merge main~3 &&
 	git merge side~1 &&
-	git checkout master &&
+	git checkout main &&
 	git merge tangle &&
 	git checkout -b reach &&
 	test_commit reach &&
-	git checkout master &&
+	git checkout main &&
 	git checkout -b octopus-a &&
 	test_commit octopus-a &&
-	git checkout master &&
+	git checkout main &&
 	git checkout -b octopus-b &&
 	test_commit octopus-b &&
-	git checkout master &&
+	git checkout main &&
 	test_commit seventh &&
 	git merge octopus-a octopus-b &&
 	git merge reach
@@ -650,7 +653,7 @@
 |\
 | *   Merge branch 'side' (early part) into tangle
 | |\
-| * \   Merge branch 'master' (early part) into tangle
+| * \   Merge branch 'main' (early part) into tangle
 | |\ \
 | * | | tangle-a
 * | | |   Merge branch 'side'
@@ -794,7 +797,7 @@
 
 test_expect_success 'decorate-refs-exclude with glob' '
 	cat >expect.decorate <<-\EOF &&
-	Merge-tag-reach (HEAD -> master)
+	Merge-tag-reach (HEAD -> main)
 	Merge-tags-octopus-a-and-octopus-b
 	seventh (tag: seventh)
 	octopus-b (tag: octopus-b)
@@ -811,7 +814,7 @@
 
 test_expect_success 'decorate-refs-exclude without globs' '
 	cat >expect.decorate <<-\EOF &&
-	Merge-tag-reach (HEAD -> master)
+	Merge-tag-reach (HEAD -> main)
 	Merge-tags-octopus-a-and-octopus-b
 	seventh (tag: seventh)
 	octopus-b (tag: octopus-b, octopus-b)
@@ -828,7 +831,7 @@
 
 test_expect_success 'multiple decorate-refs-exclude' '
 	cat >expect.decorate <<-\EOF &&
-	Merge-tag-reach (HEAD -> master)
+	Merge-tag-reach (HEAD -> main)
 	Merge-tags-octopus-a-and-octopus-b
 	seventh (tag: seventh)
 	octopus-b (tag: octopus-b)
@@ -851,7 +854,7 @@
 
 test_expect_success 'decorate-refs and decorate-refs-exclude' '
 	cat >expect.no-decorate <<-\EOF &&
-	Merge-tag-reach (master)
+	Merge-tag-reach (main)
 	Merge-tags-octopus-a-and-octopus-b
 	seventh
 	octopus-b
@@ -866,7 +869,7 @@
 
 test_expect_success 'deocrate-refs and log.excludeDecoration' '
 	cat >expect.decorate <<-\EOF &&
-	Merge-tag-reach (master)
+	Merge-tag-reach (main)
 	Merge-tags-octopus-a-and-octopus-b
 	seventh
 	octopus-b (octopus-b)
@@ -881,7 +884,7 @@
 
 test_expect_success 'decorate-refs-exclude and simplify-by-decoration' '
 	cat >expect.decorate <<-\EOF &&
-	Merge-tag-reach (HEAD -> master)
+	Merge-tag-reach (HEAD -> main)
 	reach (tag: reach, reach)
 	seventh (tag: seventh)
 	Merge-branch-tangle
@@ -1080,7 +1083,7 @@
 | |\ \  Merge: MERGE_PARENTS
 | | | | Author: A U Thor <author@example.com>
 | | | |
-| | | |     Merge branch 'master' (early part) into tangle
+| | | |     Merge branch 'main' (early part) into tangle
 | | | |
 | * | | commit COMMIT_OBJECT_NAME
 | | | | Author: A U Thor <author@example.com>
@@ -1355,7 +1358,7 @@
 *** | |\ \  Merge: MERGE_PARENTS
 *** | | | | Author: A U Thor <author@example.com>
 *** | | | |
-*** | | | |     Merge branch 'master' (early part) into tangle
+*** | | | |     Merge branch 'main' (early part) into tangle
 *** | | | |
 *** | * | | commit COMMIT_OBJECT_NAME
 *** | | | | Author: A U Thor <author@example.com>
@@ -1588,24 +1591,24 @@
 '
 
 test_expect_success GPG 'setup signed branch' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b signed master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b signed main &&
 	echo foo >foo &&
 	git add foo &&
 	git commit -S -m signed_commit
 '
 
 test_expect_success GPG 'setup signed branch with subkey' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b signed-subkey master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b signed-subkey main &&
 	echo foo >foo &&
 	git add foo &&
 	git commit -SB7227189 -m signed_commit
 '
 
 test_expect_success GPGSM 'setup signed branch x509' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b signed-x509 master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b signed-x509 main &&
 	echo foo >foo &&
 	git add foo &&
 	test_config gpg.format x509 &&
@@ -1638,12 +1641,12 @@
 '
 
 test_expect_success GPG 'log --graph --show-signature for merged tag' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b plain master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b plain main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged master &&
+	git checkout -b tagged main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1657,12 +1660,12 @@
 '
 
 test_expect_success GPG 'log --graph --show-signature for merged tag in shallow clone' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b plain-shallow master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b plain-shallow main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout --detach master &&
+	git checkout --detach main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1677,12 +1680,12 @@
 '
 
 test_expect_success GPG 'log --graph --show-signature for merged tag with missing key' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b plain-nokey master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b plain-nokey main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-nokey master &&
+	git checkout -b tagged-nokey main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1696,12 +1699,12 @@
 '
 
 test_expect_success GPG 'log --graph --show-signature for merged tag with bad signature' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b plain-bad master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b plain-bad main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-bad master &&
+	git checkout -b tagged-bad main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1718,12 +1721,12 @@
 '
 
 test_expect_success GPG 'log --show-signature for merged tag with GPG failure' '
-	test_when_finished "git reset --hard && git checkout master" &&
-	git checkout -b plain-fail master &&
+	test_when_finished "git reset --hard && git checkout main" &&
+	git checkout -b plain-fail main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-fail master &&
+	git checkout -b tagged-fail main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1737,14 +1740,14 @@
 '
 
 test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' '
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	test_config gpg.format x509 &&
 	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
-	git checkout -b plain-x509 master &&
+	git checkout -b plain-x509 main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-x509 master &&
+	git checkout -b tagged-x509 main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1758,14 +1761,14 @@
 '
 
 test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 missing key' '
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	test_config gpg.format x509 &&
 	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
-	git checkout -b plain-x509-nokey master &&
+	git checkout -b plain-x509-nokey main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-x509-nokey master &&
+	git checkout -b tagged-x509-nokey main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1778,14 +1781,14 @@
 '
 
 test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 bad signature' '
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	test_config gpg.format x509 &&
 	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
-	git checkout -b plain-x509-bad master &&
+	git checkout -b plain-x509-bad main &&
 	echo aaa >bar &&
 	git add bar &&
 	git commit -m bar_commit &&
-	git checkout -b tagged-x509-bad master &&
+	git checkout -b tagged-x509-bad main &&
 	echo bbb >baz &&
 	git add baz &&
 	git commit -m baz_commit &&
@@ -1835,7 +1838,7 @@
 	git init empty &&
 	test_must_fail git -C empty log 2>stderr &&
 	test_i18ngrep does.not.have.any.commits stderr &&
-	echo 1234abcd >empty/.git/refs/heads/master &&
+	echo 1234abcd >empty/.git/refs/heads/main &&
 	test_must_fail git -C empty log 2>stderr &&
 	test_i18ngrep broken stderr &&
 	echo "ref: refs/heads/invalid.lock" >empty/.git/HEAD &&
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 586c3a8..93caf9a 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -2,30 +2,14 @@
 
 test_description='.mailmap configurations'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
-fuzz_blame () {
-	sed "
-		s/$_x05[0-9a-f][0-9a-f][0-9a-f]/OBJID/g
-		s/$_x05[0-9a-f][0-9a-f]/OBJI/g
-		s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
-	" "$@"
-}
-
-test_expect_success setup '
-	cat >contacts <<- EOF &&
-	$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
-	nick1 <bugs@company.xx>
-	EOF
-
-	echo one >one &&
-	git add one &&
-	test_tick &&
-	git commit -m initial &&
-	echo two >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "nick1 <bugs@company.xx>" -m second
+test_expect_success 'setup commits and contacts file' '
+	test_commit initial one one &&
+	test_commit --author "nick1 <bugs@company.xx>" --append second one two
 '
 
 test_expect_success 'check-mailmap no arguments' '
@@ -33,7 +17,7 @@
 '
 
 test_expect_success 'check-mailmap arguments' '
-	cat >expect <<- EOF &&
+	cat >expect <<-EOF &&
 	$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 	nick1 <bugs@company.xx>
 	EOF
@@ -44,172 +28,256 @@
 '
 
 test_expect_success 'check-mailmap --stdin' '
-	cat >expect <<- EOF &&
+	cat >expect <<-EOF &&
 	$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 	nick1 <bugs@company.xx>
 	EOF
-	git check-mailmap --stdin <contacts >actual &&
+	git check-mailmap --stdin <expect >actual &&
 	test_cmp expect actual
 '
 
-test_expect_success 'check-mailmap --stdin arguments' '
+test_expect_success 'check-mailmap --stdin arguments: no mapping' '
+	test_when_finished "rm contacts" &&
+	cat >contacts <<-EOF &&
+	$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+	nick1 <bugs@company.xx>
+	EOF
 	cat >expect <<-\EOF &&
 	Internal Guy <bugs@company.xy>
 	EOF
-	cat <contacts >>expect &&
+	cat contacts >>expect &&
+
 	git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \
 		<contacts >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'check-mailmap --stdin arguments: mapping' '
+	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-EOF &&
+	New Name <$GIT_AUTHOR_EMAIL>
+	EOF
+	cat >stdin <<-EOF &&
+	Old Name <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	cp .mailmap expect &&
+	git check-mailmap --stdin <stdin >actual &&
+	test_cmp expect actual &&
+
+	cat .mailmap >>expect &&
+	git check-mailmap --stdin "Another Old Name <$GIT_AUTHOR_EMAIL>" \
+		<stdin >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'check-mailmap bogus contact' '
 	test_must_fail git check-mailmap bogus
 '
 
-cat >expect << EOF
-$GIT_AUTHOR_NAME (1):
-      initial
-
-nick1 (1):
-      second
-
-EOF
+test_expect_success 'check-mailmap bogus contact --stdin' '
+	test_must_fail git check-mailmap --stdin bogus </dev/null
+'
 
 test_expect_success 'No mailmap' '
+	cat >expect <<-EOF &&
+	$GIT_AUTHOR_NAME (1):
+	      initial
+
+	nick1 (1):
+	      second
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Repo Guy (1):
-      initial
+test_expect_success 'setup default .mailmap' '
+	cat >default.map <<-EOF
+	Repo Guy <$GIT_AUTHOR_EMAIL>
+	EOF
+'
 
-nick1 (1):
-      second
+test_expect_success 'test default .mailmap' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
 
-EOF
+	cat >expect <<-\EOF &&
+	Repo Guy (1):
+	      initial
 
-test_expect_success 'default .mailmap' '
-	echo "Repo Guy <$GIT_AUTHOR_EMAIL>" > .mailmap &&
+	nick1 (1):
+	      second
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-# Using a mailmap file in a subdirectory of the repo here, but
-# could just as well have been a file outside of the repository
-cat >expect <<\EOF
-Internal Guy (1):
-      second
-
-Repo Guy (1):
-      initial
-
-EOF
 test_expect_success 'mailmap.file set' '
-	mkdir -p internal_mailmap &&
-	echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
-	git config mailmap.file internal_mailmap/.mailmap &&
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	test_config mailmap.file internal.map &&
+	cat >internal.map <<-\EOF &&
+	Internal Guy <bugs@company.xx>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Internal Guy (1):
+	      second
+
+	Repo Guy (1):
+	      initial
+
+	EOF
 	git shortlog HEAD >actual &&
-	test_cmp expect actual
+	test_cmp expect actual &&
+
+	# The internal_mailmap/.mailmap file is an a subdirectory, but
+	# as shown here it can also be outside the repository
+	test_when_finished "rm -rf sub-repo" &&
+	git clone . sub-repo &&
+	(
+		cd sub-repo &&
+		cp ../.mailmap . &&
+		git config mailmap.file ../internal.map &&
+		git shortlog HEAD >actual &&
+		test_cmp ../expect actual
+	)
 '
 
-cat >expect <<\EOF
-External Guy (1):
-      initial
-
-Internal Guy (1):
-      second
-
-EOF
 test_expect_success 'mailmap.file override' '
-	echo "External Guy <$GIT_AUTHOR_EMAIL>" >> internal_mailmap/.mailmap &&
-	git config mailmap.file internal_mailmap/.mailmap &&
+	test_config mailmap.file internal.map &&
+	cat >internal.map <<-EOF &&
+	Internal Guy <bugs@company.xx>
+	External Guy <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	cat >expect <<-\EOF &&
+	External Guy (1):
+	      initial
+
+	Internal Guy (1):
+	      second
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Repo Guy (1):
-      initial
-
-nick1 (1):
-      second
-
-EOF
-
 test_expect_success 'mailmap.file non-existent' '
-	rm internal_mailmap/.mailmap &&
-	rmdir internal_mailmap &&
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	cat >expect <<-\EOF &&
+	Repo Guy (1):
+	      initial
+
+	nick1 (1):
+	      second
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Internal Guy (1):
-      second
-
-Repo Guy (1):
-      initial
-
-EOF
-
 test_expect_success 'name entry after email entry' '
-	mkdir -p internal_mailmap &&
-	echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
-	echo "Internal Guy <bugs@company.xx>" >>internal_mailmap/.mailmap &&
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	test_config mailmap.file internal.map &&
+	cat >internal.map <<-\EOF &&
+	<bugs@company.xy> <bugs@company.xx>
+	Internal Guy <bugs@company.xx>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Internal Guy (1):
+	      second
+
+	Repo Guy (1):
+	      initial
+
+	EOF
+
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Internal Guy (1):
-      second
-
-Repo Guy (1):
-      initial
-
-EOF
-
 test_expect_success 'name entry after email entry, case-insensitive' '
-	mkdir -p internal_mailmap &&
-	echo "<bugs@company.xy> <bugs@company.xx>" >internal_mailmap/.mailmap &&
-	echo "Internal Guy <BUGS@Company.xx>" >>internal_mailmap/.mailmap &&
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	test_config mailmap.file internal.map &&
+	cat >internal.map <<-\EOF &&
+	<bugs@company.xy> <bugs@company.xx>
+	Internal Guy <BUGS@Company.xx>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Internal Guy (1):
+	      second
+
+	Repo Guy (1):
+	      initial
+
+	EOF
+	git shortlog HEAD >actual &&
+	test_cmp expect actual &&
+
+	cat >internal.map <<-\EOF &&
+	NiCk <BuGs@CoMpAnY.Xy> NICK1 <BUGS@COMPANY.XX>
+	EOF
+
+	cat >expect <<-\EOF &&
+	NiCk (1):
+	      second
+
+	Repo Guy (1):
+	      initial
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
-cat >expect << EOF
-$GIT_AUTHOR_NAME (1):
-      initial
-
-nick1 (1):
-      second
-
-EOF
 test_expect_success 'No mailmap files, but configured' '
-	rm -f .mailmap internal_mailmap/.mailmap &&
+	cat >expect <<-EOF &&
+	$GIT_AUTHOR_NAME (1):
+	      initial
+
+	nick1 (1):
+	      second
+
+	EOF
 	git shortlog HEAD >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'setup mailmap blob tests' '
 	git checkout -b map &&
-	test_when_finished "git checkout master" &&
-	cat >just-bugs <<- EOF &&
+	test_when_finished "git checkout main" &&
+	cat >just-bugs <<-\EOF &&
 	Blob Guy <bugs@company.xx>
 	EOF
-	cat >both <<- EOF &&
+	cat >both <<-EOF &&
 	Blob Guy <$GIT_AUTHOR_EMAIL>
 	Blob Guy <bugs@company.xx>
 	EOF
 	printf "Tricky Guy <$GIT_AUTHOR_EMAIL>" >no-newline &&
 	git add just-bugs both no-newline &&
 	git commit -m "my mailmaps" &&
-	echo "Repo Guy <$GIT_AUTHOR_EMAIL>" >.mailmap &&
-	echo "Internal Guy <$GIT_AUTHOR_EMAIL>" >internal.map
+
+	cat >internal.map <<-EOF
+	Internal Guy <$GIT_AUTHOR_EMAIL>
+	EOF
 '
 
 test_expect_success 'mailmap.blob set' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
 	cat >expect <<-\EOF &&
 	Blob Guy (1):
 	      second
@@ -223,6 +291,9 @@
 '
 
 test_expect_success 'mailmap.blob overrides .mailmap' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
 	cat >expect <<-\EOF &&
 	Blob Guy (2):
 	      initial
@@ -249,7 +320,11 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'mailmap.blob can be missing' '
+test_expect_success 'mailmap.file can be missing' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	test_config mailmap.file nonexistent &&
 	cat >expect <<-\EOF &&
 	Repo Guy (1):
 	      initial
@@ -258,7 +333,34 @@
 	      second
 
 	EOF
-	git -c mailmap.blob=map:nonexistent shortlog HEAD >actual &&
+	git shortlog HEAD >actual 2>err &&
+	test_must_be_empty err &&
+	test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob can be missing' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	cat >expect <<-\EOF &&
+	Repo Guy (1):
+	      initial
+
+	nick1 (1):
+	      second
+
+	EOF
+	git -c mailmap.blob=map:nonexistent shortlog HEAD >actual 2>err &&
+	test_must_be_empty err &&
+	test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob might be the wrong type' '
+	test_when_finished "rm .mailmap" &&
+	cp default.map .mailmap &&
+
+	git -c mailmap.blob=HEAD: shortlog HEAD >actual 2>err &&
+	test_i18ngrep "mailmap is not a blob" err &&
 	test_cmp expect actual
 '
 
@@ -267,11 +369,15 @@
 	(
 		cd non-bare &&
 		test_commit one .mailmap "Fake Name <$GIT_AUTHOR_EMAIL>" &&
-		echo "     1	Fake Name" >expect &&
+		cat >expect <<-\EOF &&
+		     1	Fake Name
+		EOF
 		git shortlog -ns HEAD >actual &&
 		test_cmp expect actual &&
 		rm .mailmap &&
-		echo "     1	$GIT_AUTHOR_NAME" >expect &&
+		cat >expect <<-EOF &&
+		     1	$GIT_AUTHOR_NAME
+		EOF
 		git shortlog -ns HEAD >actual &&
 		test_cmp expect actual
 	)
@@ -281,7 +387,9 @@
 	git clone --bare non-bare bare &&
 	(
 		cd bare &&
-		echo "     1	Fake Name" >expect &&
+		cat >expect <<-\EOF &&
+		     1	Fake Name
+		EOF
 		git shortlog -ns HEAD >actual &&
 		test_cmp expect actual
 	)
@@ -300,178 +408,260 @@
 	test_cmp expect actual
 '
 
-test_expect_success 'cleanup after mailmap.blob tests' '
-	rm -f .mailmap
-'
-
 test_expect_success 'single-character name' '
-	echo "     1	A <$GIT_AUTHOR_EMAIL>" >expect &&
-	echo "     1	nick1 <bugs@company.xx>" >>expect &&
-	echo "A <$GIT_AUTHOR_EMAIL>" >.mailmap &&
 	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-EOF &&
+	A <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	cat >expect <<-EOF &&
+	     1	A <$GIT_AUTHOR_EMAIL>
+	     1	nick1 <bugs@company.xx>
+	EOF
 	git shortlog -es HEAD >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'preserve canonical email case' '
-	echo "     1	$GIT_AUTHOR_NAME <AUTHOR@example.com>" >expect &&
-	echo "     1	nick1 <bugs@company.xx>" >>expect &&
-	echo "<AUTHOR@example.com> <$GIT_AUTHOR_EMAIL>" >.mailmap &&
 	test_when_finished "rm .mailmap" &&
+	cat >.mailmap <<-EOF &&
+	<AUTHOR@example.com> <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	cat >expect <<-EOF &&
+	     1	$GIT_AUTHOR_NAME <AUTHOR@example.com>
+	     1	nick1 <bugs@company.xx>
+	EOF
 	git shortlog -es HEAD >actual &&
 	test_cmp expect actual
 '
 
-# Extended mailmap configurations should give us the following output for shortlog
-cat >expect << EOF
-$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> (1):
-      initial
+test_expect_success 'gitmailmap(5) example output: setup' '
+	test_create_repo doc &&
+	test_commit -C doc --author "Joe Developer <joe@example.com>" A &&
+	test_commit -C doc --author "Joe R. Developer <joe@example.com>" B &&
+	test_commit -C doc --author "Jane Doe <jane@example.com>" C &&
+	test_commit -C doc --author "Jane Doe <jane@laptop.(none)>" D &&
+	test_commit -C doc --author "Jane D. <jane@desktop.(none)>" E
+'
 
-CTO <cto@company.xx> (1):
-      seventh
+test_expect_success 'gitmailmap(5) example output: example #1' '
+	test_config -C doc mailmap.file ../doc.map &&
+	cat >doc.map <<-\EOF &&
+	Joe R. Developer <joe@example.com>
+	Jane Doe <jane@example.com>
+	Jane Doe <jane@desktop.(none)>
+	EOF
 
-Other Author <other@author.xx> (2):
-      third
-      fourth
+	cat >expect <<-\EOF &&
+	Author Joe Developer <joe@example.com> maps to Joe R. Developer <joe@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-Santa Claus <santa.claus@northpole.xx> (2):
-      fifth
-      sixth
+	Author Joe R. Developer <joe@example.com> maps to Joe R. Developer <joe@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-Some Dude <some@dude.xx> (1):
-      second
+	Author Jane Doe <jane@example.com> maps to Jane Doe <jane@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
 
-EOF
+	Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@laptop.(none)>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@desktop.(none)>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'gitmailmap(5) example output: example #2' '
+	test_config -C doc mailmap.file ../doc.map &&
+	cat >doc.map <<-\EOF &&
+	Joe R. Developer <joe@example.com>
+	Jane Doe <jane@example.com> <jane@laptop.(none)>
+	Jane Doe <jane@example.com> <jane@desktop.(none)>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Author Joe Developer <joe@example.com> maps to Joe R. Developer <joe@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Joe R. Developer <joe@example.com> maps to Joe R. Developer <joe@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Jane Doe <jane@example.com> maps to Jane Doe <jane@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Jane Doe <jane@laptop.(none)> maps to Jane Doe <jane@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Jane D <jane@desktop.(none)> maps to Jane Doe <jane@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'gitmailmap(5) example output: example #3' '
+	test_config -C doc mailmap.file ../doc.map &&
+	cat >>doc.map <<-\EOF &&
+	Joe R. Developer <joe@example.com> Joe <bugs@example.com>
+	Jane Doe <jane@example.com> Jane <bugs@example.com>
+	EOF
+
+	test_commit -C doc --author "Joe <bugs@example.com>" F &&
+	test_commit -C doc --author "Jane <bugs@example.com>" G &&
+
+	cat >>expect <<-\EOF &&
+
+	Author Joe <bugs@example.com> maps to Joe R. Developer <joe@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author Jane <bugs@example.com> maps to Jane Doe <jane@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C doc log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
 
 test_expect_success 'Shortlog output (complex mapping)' '
-	echo three >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "nick2 <bugs@company.xx>" -m third &&
+	test_config mailmap.file complex.map &&
+	cat >complex.map <<-EOF &&
+	Committed <$GIT_COMMITTER_EMAIL>
+	<cto@company.xx> <cto@coompany.xx>
+	Some Dude <some@dude.xx>         nick1 <bugs@company.xx>
+	Other Author <other@author.xx>   nick2 <bugs@company.xx>
+	Other Author <other@author.xx>         <nick2@company.xx>
+	Santa Claus <santa.claus@northpole.xx> <me@company.xx>
+	EOF
 
-	echo four >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "nick2 <nick2@company.xx>" -m fourth &&
+	test_commit --author "nick2 <bugs@company.xx>" --append third one three &&
+	test_commit --author "nick2 <nick2@company.xx>" --append fourth one four &&
+	test_commit --author "santa <me@company.xx>" --append fifth one five &&
+	test_commit --author "claus <me@company.xx>" --append sixth one six &&
+	test_commit --author "CTO <cto@coompany.xx>" --append seventh one seven &&
 
-	echo five >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "santa <me@company.xx>" -m fifth &&
+	cat >expect <<-EOF &&
+	$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> (1):
+	      initial
 
-	echo six >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "claus <me@company.xx>" -m sixth &&
+	CTO <cto@company.xx> (1):
+	      seventh
 
-	echo seven >>one &&
-	git add one &&
-	test_tick &&
-	git commit --author "CTO <cto@coompany.xx>" -m seventh &&
+	Other Author <other@author.xx> (2):
+	      third
+	      fourth
 
-	mkdir -p internal_mailmap &&
-	echo "Committed <$GIT_COMMITTER_EMAIL>" > internal_mailmap/.mailmap &&
-	echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
-	echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
-	echo "Other Author <other@author.xx>   nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
-	echo "Other Author <other@author.xx>         <nick2@company.xx>" >> internal_mailmap/.mailmap &&
-	echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
-	echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+	Santa Claus <santa.claus@northpole.xx> (2):
+	      fifth
+	      sixth
+
+	Some Dude <some@dude.xx> (1):
+	      second
+
+	EOF
 
 	git shortlog -e HEAD >actual &&
 	test_cmp expect actual
 
 '
 
-# git log with --pretty format which uses the name and email mailmap placemarkers
-cat >expect << EOF
-Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-
-Author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> maps to $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
-Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
-EOF
-
 test_expect_success 'Log output (complex mapping)' '
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-EOF &&
+	Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+
+	Author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> maps to $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+	Committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> maps to Committed <$GIT_COMMITTER_EMAIL>
+	EOF
+
 	git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
 	test_cmp expect actual
 '
 
-cat >expect << EOF
-Author email cto@coompany.xx has local-part cto
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email me@company.xx has local-part me
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email me@company.xx has local-part me
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email nick2@company.xx has local-part nick2
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email bugs@company.xx has local-part bugs
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email bugs@company.xx has local-part bugs
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-
-Author email author@example.com has local-part author
-Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
-EOF
-
 test_expect_success 'Log output (local-part email address)' '
+	cat >expect <<-EOF &&
+	Author email cto@coompany.xx has local-part cto
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email me@company.xx has local-part me
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email me@company.xx has local-part me
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email nick2@company.xx has local-part nick2
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email bugs@company.xx has local-part bugs
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email bugs@company.xx has local-part bugs
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+
+	Author email author@example.com has local-part author
+	Committer email $GIT_COMMITTER_EMAIL has local-part $TEST_COMMITTER_LOCALNAME
+	EOF
+
 	git log --pretty=format:"Author email %ae has local-part %al%nCommitter email %ce has local-part %cl%n" >actual &&
 	test_cmp expect actual
 '
 
-cat >expect << EOF
-Author: CTO <cto@company.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Other Author <other@author.xx>
-Author: Other Author <other@author.xx>
-Author: Some Dude <some@dude.xx>
-Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
-EOF
-
 test_expect_success 'Log output with --use-mailmap' '
-	git log --use-mailmap | grep Author >actual &&
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-EOF &&
+	Author: CTO <cto@company.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Other Author <other@author.xx>
+	Author: Other Author <other@author.xx>
+	Author: Some Dude <some@dude.xx>
+	Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	git log --use-mailmap >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
-cat >expect << EOF
-Author: CTO <cto@company.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Other Author <other@author.xx>
-Author: Other Author <other@author.xx>
-Author: Some Dude <some@dude.xx>
-Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
-EOF
-
 test_expect_success 'Log output with log.mailmap' '
-	git -c log.mailmap=True log | grep Author >actual &&
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-EOF &&
+	Author: CTO <cto@company.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Other Author <other@author.xx>
+	Author: Other Author <other@author.xx>
+	Author: Some Dude <some@dude.xx>
+	Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+	EOF
+
+	git -c log.mailmap=True log >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'log.mailmap=false disables mailmap' '
-	cat >expect <<- EOF &&
+	cat >expect <<-EOF &&
 	Author: CTO <cto@coompany.xx>
 	Author: claus <me@company.xx>
 	Author: santa <me@company.xx>
@@ -480,12 +670,13 @@
 	Author: nick1 <bugs@company.xx>
 	Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 	EOF
-	git -c log.mailmap=False log | grep Author > actual &&
+	git -c log.mailmap=false log >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success '--no-use-mailmap disables mailmap' '
-	cat >expect <<- EOF &&
+	cat >expect <<-EOF &&
 	Author: CTO <cto@coompany.xx>
 	Author: claus <me@company.xx>
 	Author: santa <me@company.xx>
@@ -494,64 +685,251 @@
 	Author: nick1 <bugs@company.xx>
 	Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
 	EOF
-	git log --no-use-mailmap | grep Author > actual &&
+	git log --no-use-mailmap >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
-cat >expect <<\EOF
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-EOF
-
 test_expect_success 'Grep author with --use-mailmap' '
-	git log --use-mailmap --author Santa | grep Author >actual &&
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-\EOF &&
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	EOF
+	git log --use-mailmap --author Santa >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
-cat >expect <<\EOF
-Author: Santa Claus <santa.claus@northpole.xx>
-Author: Santa Claus <santa.claus@northpole.xx>
-EOF
 
 test_expect_success 'Grep author with log.mailmap' '
-	git -c log.mailmap=True log --author Santa | grep Author >actual &&
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-\EOF &&
+	Author: Santa Claus <santa.claus@northpole.xx>
+	Author: Santa Claus <santa.claus@northpole.xx>
+	EOF
+
+	git -c log.mailmap=True log --author Santa >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'log.mailmap is true by default these days' '
-	git log --author Santa | grep Author >actual &&
+	test_config mailmap.file complex.map &&
+	git log --author Santa >log &&
+	grep Author log >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'Only grep replaced author with --use-mailmap' '
+	test_config mailmap.file complex.map &&
 	git log --use-mailmap --author "<cto@coompany.xx>" >actual &&
 	test_must_be_empty actual
 '
 
-# git blame
-cat >expect <<EOF
-^OBJI ($GIT_AUTHOR_NAME     DATE 1) one
-OBJID (Some Dude    DATE 2) two
-OBJID (Other Author DATE 3) three
-OBJID (Other Author DATE 4) four
-OBJID (Santa Claus  DATE 5) five
-OBJID (Santa Claus  DATE 6) six
-OBJID (CTO          DATE 7) seven
-EOF
-test_expect_success 'Blame output (complex mapping)' '
-	git blame one >actual &&
-	fuzz_blame actual >actual.fuzz &&
+test_expect_success 'Blame --porcelain output (complex mapping)' '
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-EOF &&
+	1 1 1
+	A U Thor
+	2 2 1
+	Some Dude
+	3 3 1
+	Other Author
+	4 4 1
+	Other Author
+	5 5 1
+	Santa Claus
+	6 6 1
+	Santa Claus
+	7 7 1
+	CTO
+	EOF
+
+	git blame --porcelain one >actual.blame &&
+
+	NUM="[0-9][0-9]*" &&
+	sed -n <actual.blame >actual.fuzz \
+		-e "s/^author //p" \
+		-e "s/^$OID_REGEX \\($NUM $NUM $NUM\\)$/\\1/p"  &&
 	test_cmp expect actual.fuzz
 '
 
-cat >expect <<\EOF
-Some Dude <some@dude.xx>
-EOF
+test_expect_success 'Blame output (complex mapping)' '
+	git -c mailmap.file=complex.map blame one >a &&
+	git blame one >b &&
+	test_file_not_empty a &&
+	! cmp a b
+'
 
 test_expect_success 'commit --author honors mailmap' '
+	test_config mailmap.file complex.map &&
+
+	cat >expect <<-\EOF &&
+	Some Dude <some@dude.xx>
+	EOF
+
 	test_must_fail git commit --author "nick" --allow-empty -meight &&
 	git commit --author "Some Dude" --allow-empty -meight &&
 	git show --pretty=format:"%an <%ae>%n" >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success 'comment syntax: setup' '
+	test_create_repo comm &&
+	test_commit -C comm --author "A <a@example.com>" A &&
+	test_commit -C comm --author "B <b@example.com>" B &&
+	test_commit -C comm --author "C <#@example.com>" C &&
+	test_commit -C comm --author "D <d@e#ample.com>" D &&
+
+	test_config -C comm mailmap.file ../doc.map &&
+	cat >>doc.map <<-\EOF &&
+	# Ah <a@example.com>
+
+	; Bee <b@example.com>
+	Cee <cee@example.com> <#@example.com>
+	Dee <dee@example.com> <d@e#ample.com>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Author A <a@example.com> maps to A <a@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author B <b@example.com> maps to ; Bee <b@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author C <#@example.com> maps to Cee <cee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author D <d@e#ample.com> maps to Dee <dee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C comm log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'whitespace syntax: setup' '
+	test_create_repo space &&
+	test_commit -C space --author "A <a@example.com>" A &&
+	test_commit -C space --author "B <b@example.com>" B &&
+	test_commit -C space --author " C <c@example.com>" C &&
+	test_commit -C space --author " D  <d@example.com>" D &&
+	test_commit -C space --author "E E <e@example.com>" E &&
+	test_commit -C space --author "F  F <f@example.com>" F &&
+	test_commit -C space --author "G   G <g@example.com>" G &&
+	test_commit -C space --author "H   H <h@example.com>" H &&
+
+	test_config -C space mailmap.file ../space.map &&
+	cat >>space.map <<-\EOF &&
+	Ah <ah@example.com> < a@example.com >
+	Bee <bee@example.com  > <  b@example.com  >
+	Cee <cee@example.com> C <c@example.com>
+	dee <dee@example.com>  D  <d@example.com>
+	eee <eee@example.com> E E <e@example.com>
+	eff <eff@example.com> F  F <f@example.com>
+	gee <gee@example.com> G   G <g@example.com>
+	aitch <aitch@example.com> H  H <h@example.com>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Author A <a@example.com> maps to A <a@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author B <b@example.com> maps to B <b@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author C <c@example.com> maps to Cee <cee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author D <d@example.com> maps to dee <dee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author E E <e@example.com> maps to eee <eee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author F  F <f@example.com> maps to eff <eff@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author G   G <g@example.com> maps to gee <gee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author H   H <h@example.com> maps to H   H <h@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C space log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'empty syntax: setup' '
+	test_create_repo empty &&
+	test_commit -C empty --author "A <>" A &&
+	test_commit -C empty --author "B <b@example.com>" B &&
+	test_commit -C empty --author "C <c@example.com>" C &&
+
+	test_config -C empty mailmap.file ../empty.map &&
+	cat >>empty.map <<-\EOF &&
+	Ah <ah@example.com> <>
+	Bee <bee@example.com> <>
+	Cee <> <c@example.com>
+	EOF
+
+	cat >expect <<-\EOF &&
+	Author A <> maps to Bee <bee@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author B <b@example.com> maps to B <b@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+
+	Author C <c@example.com> maps to C <c@example.com>
+	Committer C O Mitter <committer@example.com> maps to C O Mitter <committer@example.com>
+	EOF
+	git -C empty log --reverse --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'set up mailmap location tests' '
+	git init --bare loc-bare &&
+	git --git-dir=loc-bare --work-tree=. commit \
+		--allow-empty -m foo --author="Orig <orig@example.com>" &&
+	echo "New <new@example.com> <orig@example.com>" >loc-bare/.mailmap
+'
+
+test_expect_success 'bare repo with --work-tree finds mailmap at top-level' '
+	git -C loc-bare --work-tree=. log -1 --format=%aE >actual &&
+	echo new@example.com >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'bare repo does not look in current directory' '
+	git -C loc-bare log -1 --format=%aE >actual &&
+	echo orig@example.com >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'non-git shortlog respects mailmap in current dir' '
+	git --git-dir=loc-bare log -1 >input &&
+	nongit cp "$TRASH_DIRECTORY/loc-bare/.mailmap" . &&
+	nongit git shortlog -s <input >actual &&
+	echo "     1	New" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'shortlog on stdin respects mailmap from repo' '
+	cp loc-bare/.mailmap . &&
+	git shortlog -s <input >actual &&
+	echo "     1	New" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'find top-level mailmap from subdir' '
+	git clone loc-bare loc-wt &&
+	cp loc-bare/.mailmap loc-wt &&
+	mkdir loc-wt/subdir &&
+	git -C loc-wt/subdir log -1 --format=%aE >actual &&
+	echo new@example.com >expect &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
index 8ff8bd8..f120857 100755
--- a/t/t4204-patch-id.sh
+++ b/t/t4204-patch-id.sh
@@ -2,6 +2,9 @@
 
 test_description='git patch-id'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -13,9 +16,9 @@
 	test_write_lines $as b >foo &&
 	test_write_lines $as b >bar &&
 	git commit -a -m first &&
-	git checkout -b same master &&
+	git checkout -b same main &&
 	git commit --amend -m same-msg &&
-	git checkout -b notsame master &&
+	git checkout -b notsame main &&
 	echo c >foo &&
 	echo c >bar &&
 	git commit --amend -a -m notsame-msg &&
@@ -46,31 +49,31 @@
 }
 
 test_expect_success 'patch-id detects equality' '
-	get_patch_id master &&
+	get_patch_id main &&
 	get_patch_id same &&
-	test_cmp patch-id_master patch-id_same
+	test_cmp patch-id_main patch-id_same
 '
 
 test_expect_success 'patch-id detects inequality' '
-	get_patch_id master &&
+	get_patch_id main &&
 	get_patch_id notsame &&
-	! test_cmp patch-id_master patch-id_notsame
+	! test_cmp patch-id_main patch-id_notsame
 '
 
 test_expect_success 'patch-id supports git-format-patch output' '
-	get_patch_id master &&
+	get_patch_id main &&
 	git checkout same &&
 	git format-patch -1 --stdout | calc_patch_id same &&
-	test_cmp patch-id_master patch-id_same &&
+	test_cmp patch-id_main patch-id_same &&
 	set $(git format-patch -1 --stdout | git patch-id) &&
 	test "$2" = $(git rev-parse HEAD)
 '
 
 test_expect_success 'whitespace is irrelevant in footer' '
-	get_patch_id master &&
+	get_patch_id main &&
 	git checkout same &&
 	git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
-	test_cmp patch-id_master patch-id_same
+	test_cmp patch-id_main patch-id_same
 '
 
 cmp_patch_id () {
@@ -88,7 +91,7 @@
 	shift
 	name="order-${1}-$relevant"
 	shift
-	get_top_diff "master" | calc_patch_id "$name" "$@" &&
+	get_top_diff "main" | calc_patch_id "$name" "$@" &&
 	git checkout same &&
 	git format-patch -1 --stdout -O foo-then-bar |
 		calc_patch_id "ordered-$name" "$@" &&
@@ -137,10 +140,10 @@
 '
 
 test_expect_success 'patch-id supports git-format-patch MIME output' '
-	get_patch_id master &&
+	get_patch_id main &&
 	git checkout same &&
 	git format-patch -1 --attach --stdout | calc_patch_id same &&
-	test_cmp patch-id_master patch-id_same
+	test_cmp patch-id_main patch-id_same
 '
 
 test_expect_success 'patch-id respects config from subdir' '
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index 204c149..85432b8 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -123,10 +123,10 @@
 	stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
 	printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
 	git log -z --stat --pretty="format:%s" >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
-test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' '
+test_expect_failure 'NUL termination with --stat' '
 	stat0_part=$(git diff --stat HEAD^ HEAD) &&
 	stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
 	printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
@@ -605,6 +605,12 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'pretty format %(trailers:) enables no options' '
+	git log --no-walk --pretty="%(trailers:)" >actual &&
+	# "expect" the same as the test above
+	test_cmp expect actual
+'
+
 test_expect_success '%(trailers:only) shows only "key: value" trailers' '
 	git log --no-walk --pretty="%(trailers:only)" >actual &&
 	{
@@ -709,19 +715,101 @@
 	test_cmp expect actual
 '
 
+test_expect_success '%(trailers:keyonly) shows only keys' '
+	git log --no-walk --pretty="format:%(trailers:keyonly)" >actual &&
+	test_write_lines \
+		"Signed-off-by" \
+		"Acked-by" \
+		"[ v2 updated patch description ]" \
+		"Signed-off-by" >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '%(trailers:key=foo,keyonly) shows only key' '
+	git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly)" >actual &&
+	echo "Acked-by" >expect &&
+	test_cmp expect actual
+'
+
 test_expect_success '%(trailers:key=foo,valueonly) shows only value' '
 	git log --no-walk --pretty="format:%(trailers:key=Acked-by,valueonly)" >actual &&
 	echo "A U Thor <author@example.com>" >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'pretty format %(trailers:separator) changes separator' '
-	git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual &&
-	printf "XSigned-off-by: A U Thor <author@example.com>\0Acked-by: A U Thor <author@example.com>\0[ v2 updated patch description ]\0Signed-off-by: A U Thor <author@example.com>X" >expect &&
+test_expect_success '%(trailers:valueonly) shows only values' '
+	git log --no-walk --pretty="format:%(trailers:valueonly)" >actual &&
+	test_write_lines \
+		"A U Thor <author@example.com>" \
+		"A U Thor <author@example.com>" \
+		"[ v2 updated patch description ]" \
+		"A U Thor" \
+		"  <author@example.com>" >expect &&
 	test_cmp expect actual
 '
 
-test_expect_success 'pretty format %(trailers) combining separator/key/valueonly' '
+test_expect_success '%(trailers:key=foo,keyonly,valueonly) shows nothing' '
+	git log --no-walk --pretty="format:%(trailers:key=Acked-by,keyonly,valueonly)" >actual &&
+	echo >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers:separator) changes separator' '
+	git log --no-walk --pretty=format:"X%(trailers:separator=%x00)X" >actual &&
+	(
+		printf "XSigned-off-by: A U Thor <author@example.com>\0" &&
+		printf "Acked-by: A U Thor <author@example.com>\0" &&
+		printf "[ v2 updated patch description ]\0" &&
+		printf "Signed-off-by: A U Thor\n  <author@example.com>X"
+	) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers:separator=X,unfold) changes separator' '
+	git log --no-walk --pretty=format:"X%(trailers:separator=%x00,unfold)X" >actual &&
+	(
+		printf "XSigned-off-by: A U Thor <author@example.com>\0" &&
+		printf "Acked-by: A U Thor <author@example.com>\0" &&
+		printf "[ v2 updated patch description ]\0" &&
+		printf "Signed-off-by: A U Thor <author@example.com>X"
+	) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers:key_value_separator) changes key-value separator' '
+	git log --no-walk --pretty=format:"X%(trailers:key_value_separator=%x00)X" >actual &&
+	(
+		printf "XSigned-off-by\0A U Thor <author@example.com>\n" &&
+		printf "Acked-by\0A U Thor <author@example.com>\n" &&
+		printf "[ v2 updated patch description ]\n" &&
+		printf "Signed-off-by\0A U Thor\n  <author@example.com>\nX"
+	) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers:key_value_separator,unfold) changes key-value separator' '
+	git log --no-walk --pretty=format:"X%(trailers:key_value_separator=%x00,unfold)X" >actual &&
+	(
+		printf "XSigned-off-by\0A U Thor <author@example.com>\n" &&
+		printf "Acked-by\0A U Thor <author@example.com>\n" &&
+		printf "[ v2 updated patch description ]\n" &&
+		printf "Signed-off-by\0A U Thor <author@example.com>\nX"
+	) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers:separator,key_value_separator) changes both separators' '
+	git log --no-walk --pretty=format:"%(trailers:separator=%x00,key_value_separator=%x00%x00,unfold)" >actual &&
+	(
+		printf "Signed-off-by\0\0A U Thor <author@example.com>\0" &&
+		printf "Acked-by\0\0A U Thor <author@example.com>\0" &&
+		printf "[ v2 updated patch description ]\0" &&
+		printf "Signed-off-by\0\0A U Thor <author@example.com>"
+	) >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pretty format %(trailers) combining separator/key/keyonly/valueonly' '
 	git commit --allow-empty -F - <<-\EOF &&
 	Important fix
 
@@ -748,6 +836,13 @@
 		"Does not close any tickets" \
 		"Another fix #567, #890" \
 		"Important fix #1234" >expect &&
+	test_cmp expect actual &&
+
+	git log --pretty="%s% (trailers:separator=%x2c%x20,key=Closes,keyonly)" HEAD~3.. >actual &&
+	test_write_lines \
+		"Does not close any tickets" \
+		"Another fix Closes, Closes" \
+		"Important fix Closes" >expect &&
 	test_cmp expect actual
 '
 
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index ad29e65..4871a5d 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index 60f040c..b870942 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -5,6 +5,9 @@
 
 test_description='Test for "git log --decorate" colors'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -40,13 +43,13 @@
 
 cat >expected <<EOF
 ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD ->\
- ${c_reset}${c_branch}master${c_reset}${c_commit},\
+ ${c_reset}${c_branch}main${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\
  ${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B
 ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit},\
- ${c_reset}${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
+ ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
 ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
- On master: Changes to A.t
+ On main: Changes to A.t
 ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
 EOF
 
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 6cdbe47..7f0c1dc 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -2,6 +2,9 @@
 
 test_description='magic pathspec tests using git-log'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -26,15 +29,10 @@
 '
 
 test_expect_success '"git log :/detached -- " should find a commit only in HEAD' '
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git checkout --detach &&
-	# Must manually call `test_tick` instead of using `test_commit`,
-	# because the latter additionally creates a tag, which would make
-	# the commit reachable not only via HEAD.
-	test_tick &&
-	git commit --allow-empty -m detached &&
-	test_tick &&
-	git commit --allow-empty -m something-else &&
+	test_commit --no-tag detached &&
+	test_commit --no-tag something-else &&
 	git log :/detached --
 '
 
@@ -119,7 +117,7 @@
 	git checkout HEAD^ &&
 	echo 2 >a &&
 	git commit -a -m "update a to 2" &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git add a &&
 	git log --merge -- a
 '
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 85d1514..560127c 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test log -L'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup (import history)' '
diff --git a/t/t4211/history.export b/t/t4211/history.export
index f9f41e2..006972a 100644
--- a/t/t4211/history.export
+++ b/t/t4211/history.export
@@ -316,7 +316,7 @@
 	return s;
 }
 
-commit refs/heads/master
+commit refs/heads/main
 mark :19
 author Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
 committer Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
index a080325..f70c46b 100755
--- a/t/t4214-log-graph-octopus.sh
+++ b/t/t4214-log-graph-octopus.sh
@@ -2,6 +2,9 @@
 
 test_description='git log --graph of skewed left octopus merge.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
@@ -17,7 +20,7 @@
 test_expect_success 'set up merge history' '
 	test_commit initial &&
 	for i in 1 2 3 4 ; do
-		git checkout master -b $i || return $?
+		git checkout main -b $i || return $?
 		# Make tag name different from branch name, to avoid
 		# ambiguity error when calling checkout.
 		test_commit $i $i $i tag$i || return $?
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index d11040c..50f206d 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git log for a path with Bloom filters'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 GIT_TEST_COMMIT_GRAPH=0
@@ -21,7 +24,7 @@
 	test_commit c10 file_to_be_deleted &&
 	git checkout -b side HEAD~4 &&
 	test_commit side-1 file4 &&
-	git checkout master &&
+	git checkout main &&
 	git merge side &&
 	test_commit c11 file5 &&
 	mv file5 file5_renamed &&
@@ -40,11 +43,11 @@
 '
 
 graph_read_expect () {
-	NUM_CHUNKS=5
+	NUM_CHUNKS=6
 	cat >expect <<- EOF
 	header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
 	num_commits: $1
-	chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data
+	chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
 	EOF
 	test-tool read-graph >actual &&
 	test_cmp expect actual
@@ -94,7 +97,7 @@
 		      "--topo-order" \
 		      "--date-order" \
 		      "--author-date-order" \
-		      "--ancestry-path side..master"
+		      "--ancestry-path side..main"
 	do
 		test_expect_success "git log option: $option for path: $path" '
 			test_bloom_filters_used "$option -- $path" &&
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
index 6e1b73e..0ee69d2 100755
--- a/t/t4253-am-keep-cr-dos.sh
+++ b/t/t4253-am-keep-cr-dos.sh
@@ -6,6 +6,9 @@
 test_description='git-am mbox with dos line ending.
 
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Three patches which will be added as files with dos line ending.
@@ -43,7 +46,7 @@
 
 test_expect_success 'am with dos files without --keep-cr' '
 	git checkout -b dosfiles initial &&
-	git format-patch -k initial..master &&
+	git format-patch -k initial..main &&
 	test_must_fail git am -k -3 000*.patch &&
 	git am --abort &&
 	rm -rf .git/rebase-apply 000*.patch
@@ -51,23 +54,23 @@
 
 test_expect_success 'am with dos files with --keep-cr' '
 	git checkout -b dosfiles-keep-cr initial &&
-	git format-patch -k --stdout initial..master >output &&
+	git format-patch -k --stdout initial..main >output &&
 	git am --keep-cr -k -3 output &&
-	git diff --exit-code master
+	git diff --exit-code main
 '
 
 test_expect_success 'am with dos files config am.keepcr' '
 	git config am.keepcr 1 &&
 	git checkout -b dosfiles-conf-keepcr initial &&
-	git format-patch -k --stdout initial..master >output &&
+	git format-patch -k --stdout initial..main >output &&
 	git am -k -3 output &&
-	git diff --exit-code master
+	git diff --exit-code main
 '
 
 test_expect_success 'am with dos files config am.keepcr overridden by --no-keep-cr' '
 	git config am.keepcr 1 &&
 	git checkout -b dosfiles-conf-keepcr-override initial &&
-	git format-patch -k initial..master &&
+	git format-patch -k initial..main &&
 	test_must_fail git am -k -3 --no-keep-cr 000*.patch &&
 	git am --abort &&
 	rm -rf .git/rebase-apply 000*.patch
@@ -75,14 +78,14 @@
 
 test_expect_success 'am with dos files with --keep-cr continue' '
 	git checkout -b dosfiles-keep-cr-continue initial &&
-	git format-patch -k initial..master &&
+	git format-patch -k initial..main &&
 	append_cr <file1a >file &&
 	git commit -m "different patch" file &&
 	test_must_fail git am --keep-cr -k -3 000*.patch &&
 	append_cr <file2 >file &&
 	git add file &&
 	git am -3 --resolved &&
-	git diff --exit-code master
+	git diff --exit-code main
 '
 
 test_expect_success 'am with unix files config am.keepcr overridden by --no-keep-cr' '
@@ -90,9 +93,9 @@
 	git checkout -b unixfiles-conf-keepcr-override initial &&
 	cp -f file1 file &&
 	git commit -m "line ending to unix" file &&
-	git format-patch -k initial..master &&
+	git format-patch -k initial..main &&
 	git am -k -3 --no-keep-cr 000*.patch &&
-	git diff --exit-code -w master
+	git diff --exit-code -w main
 '
 
 test_done
diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh
index daf01c3..54be7da 100755
--- a/t/t4254-am-corrupt.sh
+++ b/t/t4254-am-corrupt.sh
@@ -60,7 +60,7 @@
 	test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
 	echo "error: git diff header lacks filename information (line 4)" >expected &&
 	test_path_is_file f &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success "NUL in commit message's body" '
diff --git a/t/t4257-am-interactive.sh b/t/t4257-am-interactive.sh
index 5344bd2..aed8f4d 100755
--- a/t/t4257-am-interactive.sh
+++ b/t/t4257-am-interactive.sh
@@ -10,7 +10,7 @@
 	git format-patch --stdout -2 >mbox &&
 
 	git reset --hard unrelated &&
-	test_commit conflict-master file master base
+	test_commit conflict-main file main base
 '
 
 # Sanity check our setup.
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 3ebb0d3..7204799 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -431,15 +431,33 @@
 	test_cmp expect actual
 '
 
-test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future (2^34 - 1) commit' '
 	rm -f .git/index &&
-	echo content >file &&
+	echo foo >file &&
 	git add file &&
-	GIT_COMMITTER_DATE="@68719476737 +0000" \
+	GIT_COMMITTER_DATE="@17179869183 +0000" \
 		git commit -m "tempori parendum"
 '
 
-test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with far-future mtime' '
+	git archive HEAD >future.tar
+'
+
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
+	echo 2514 >expect &&
+	tar_info future.tar | cut -d" " -f2 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success TIME_IS_64BIT 'set up repository with far-far-future (2^36 + 1) commit' '
+	rm -f .git/index &&
+	echo content >file &&
+	git add file &&
+	GIT_TEST_COMMIT_GRAPH=0 GIT_COMMITTER_DATE="@68719476737 +0000" \
+		git commit -m "tempori parendum"
+'
+
+test_expect_success TIME_IS_64BIT 'generate tar with far-far-future mtime' '
 	git archive HEAD >future.tar
 '
 
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index 3e7b23c..2d32d0e 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -153,7 +153,8 @@
 
 	# check the number of entries in the ZIP file directory
 	expr 65536 + 256 >expect &&
-	"$ZIPINFO" many.zip | head -2 | sed -n "2s/.* //p" >actual &&
+	"$ZIPINFO" -h many.zip >zipinfo &&
+	sed -n "2s/.* //p" <zipinfo >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
index c1811ea..cb67bac 100755
--- a/t/t5150-request-pull.sh
+++ b/t/t5150-request-pull.sh
@@ -2,6 +2,9 @@
 
 test_description='Test workflows involving pull request.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq PERL
@@ -31,13 +34,13 @@
 		test_tick &&
 		git commit -m "\"Thirty days\", a reminder of month lengths" &&
 		git tag -m "version 1" -a initial &&
-		git push --tags origin master
+		git push --tags origin main
 	) &&
 	(
 		cd local &&
 		git remote add upstream "$trash_url/upstream.git" &&
 		git fetch upstream &&
-		git pull upstream master &&
+		git pull upstream main &&
 		cat <<-\EOT >>mnemonic.txt &&
 		Of twyecescore-eightt is but eine,
 		And all the remnante be thrycescore-eine.
@@ -59,7 +62,7 @@
 		git commit -a -m "Adapt to use modern, simpler English
 
 But keep the old version, too, in case some people prefer it." &&
-		git checkout master
+		git checkout main
 	)
 
 '
@@ -129,7 +132,7 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
+		git merge --ff-only main &&
 		test_must_fail git request-pull initial "$downstream_url" \
 			2>../err
 	) &&
@@ -145,9 +148,9 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
-		git push origin master:for-upstream &&
-		git request-pull initial origin master:for-upstream >../request
+		git merge --ff-only main &&
+		git push origin main:for-upstream &&
+		git request-pull initial origin main:for-upstream >../request
 	) &&
 	sed -nf read-request.sed <request >digest &&
 	{
@@ -172,9 +175,9 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
-		git push --tags origin master simplify &&
-		git push origin master:for-upstream &&
+		git merge --ff-only main &&
+		git push --tags origin main simplify &&
+		git push origin main:for-upstream &&
 		git request-pull initial "$downstream_url" >../request
 	) &&
 	sed -nf read-request.sed <request >digest &&
@@ -215,19 +218,19 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
+		git merge --ff-only main &&
 		git push origin tags/full &&
 		git request-pull initial "$downstream_url" tags/full >../request
 	) &&
 	<request sed -nf fuzz.sed >request.fuzzy &&
-	test_i18ncmp expect request.fuzzy &&
+	test_cmp expect request.fuzzy &&
 
 	(
 		cd local &&
 		git request-pull initial "$downstream_url" tags/full:refs/tags/full
 	) >request &&
 	sed -nf fuzz.sed <request >request.fuzzy &&
-	test_i18ncmp expect request.fuzzy &&
+	test_cmp expect request.fuzzy &&
 
 	(
 		cd local &&
@@ -243,9 +246,9 @@
 		OPTIONS_KEEPDASHDASH=Yes &&
 		export OPTIONS_KEEPDASHDASH &&
 		git checkout initial &&
-		git merge --ff-only master &&
-		git push origin master:for-upstream &&
-		git request-pull -- initial "$downstream_url" master:for-upstream >../request
+		git merge --ff-only main &&
+		git push origin main:for-upstream &&
+		git request-pull -- initial "$downstream_url" main:for-upstream >../request
 	)
 
 '
@@ -257,7 +260,7 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
+		git merge --ff-only main &&
 		git tag -mrelease v2.0 &&
 		git push origin refs/tags/v2.0:refs/tags/v2-0 &&
 		test_must_fail git request-pull initial "$downstream_url" tags/v2.0 \
@@ -275,7 +278,7 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
+		git merge --ff-only main &&
 		git push origin HEAD:refs/tags/full &&
 		test_must_fail git request-pull initial "$downstream_url" tags/full \
 			2>../err
@@ -292,7 +295,7 @@
 	(
 		cd local &&
 		git checkout initial &&
-		git merge --ff-only master &&
+		git merge --ff-only main &&
 		git push origin refs/tags/full &&
 		git tag -f -m"Thirty-one days" full &&
 		test_must_fail git request-pull initial "$downstream_url" tags/full \
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 392201c..d586fdc 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -427,7 +427,7 @@
 	test_path_is_file foo.idx
 '
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'index-pack --threads=N or pack.threads=N warns when no pthreads' '
 	test_must_fail git index-pack --threads=2 2>err &&
 	grep ^warning: err >warnings &&
 	test_line_count = 1 warnings &&
@@ -445,7 +445,7 @@
 	grep -F "no threads support, ignoring pack.threads" err
 '
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '
 	git pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err &&
 	grep ^warning: err >warnings &&
 	test_line_count = 1 warnings &&
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index df60f18..b447ce5 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='prune'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 day=$((60*60*24))
@@ -119,7 +122,7 @@
 test_expect_success 'prune: prune former HEAD after checking out branch' '
 
 	head_oid=$(git rev-parse HEAD) &&
-	git checkout --quiet master &&
+	git checkout --quiet main &&
 	git prune -v >prune_actual &&
 	grep "$head_oid" prune_actual
 
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
index a5eca21..44bd9ef 100755
--- a/t/t5305-include-tag.sh
+++ b/t/t5305-include-tag.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git pack-object --include-tag'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 TRASH=$(pwd)
@@ -111,7 +114,7 @@
 
 test_expect_success 'single-branch clone can transfer tag' '
 	rm -rf clone.git &&
-	git clone --no-local --single-branch -b master . clone.git &&
+	git clone --no-local --single-branch -b main . clone.git &&
 	git -C clone.git fsck
 '
 
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 1d40fca..40b9f63 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -1,7 +1,12 @@
 #!/bin/sh
 
 test_description='exercise basic bitmap functionality'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
 
 objpath () {
 	echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
@@ -20,84 +25,172 @@
 	grep -Ff "$1" "$2"
 }
 
+# To ensure the logic for "maximal commits" is exercised, make
+# the repository a bit more complicated.
+#
+#    other                         second
+#      *                             *
+# (99 commits)                  (99 commits)
+#      *                             *
+#      |\                           /|
+#      | * octo-other  octo-second * |
+#      |/|\_________  ____________/|\|
+#      | \          \/  __________/  |
+#      |  | ________/\ /             |
+#      *  |/          * merge-right  *
+#      | _|__________/ \____________ |
+#      |/ |                         \|
+# (l1) *  * merge-left               * (r1)
+#      | / \________________________ |
+#      |/                           \|
+# (l2) *                             * (r2)
+#       \___________________________ |
+#                                   \|
+#                                    * (base)
+#
+# We only push bits down the first-parent history, which
+# makes some of these commits unimportant!
+#
+# The important part for the maximal commit algorithm is how
+# the bitmasks are extended. Assuming starting bit positions
+# for second (bit 0) and other (bit 1), the bitmasks at the
+# end should be:
+#
+#      second: 1       (maximal, selected)
+#       other: 01      (maximal, selected)
+#      (base): 11 (maximal)
+#
+# This complicated history was important for a previous
+# version of the walk that guarantees never walking a
+# commit multiple times. That goal might be important
+# again, so preserve this complicated case. For now, this
+# test will guarantee that the bitmaps are computed
+# correctly, even with the repeat calculations.
+
 test_expect_success 'setup repo with moderate-sized history' '
-	test_commit_bulk --id=file 100 &&
+	test_commit_bulk --id=file 10 &&
+	git branch -M second &&
 	git checkout -b other HEAD~5 &&
 	test_commit_bulk --id=side 10 &&
-	git checkout master &&
-	bitmaptip=$(git rev-parse master) &&
+
+	# add complicated history setup, including merges and
+	# ambiguous merge-bases
+
+	git checkout -b merge-left other~2 &&
+	git merge second~2 -m "merge-left" &&
+
+	git checkout -b merge-right second~1 &&
+	git merge other~1 -m "merge-right" &&
+
+	git checkout -b octo-second second &&
+	git merge merge-left merge-right -m "octopus-second" &&
+
+	git checkout -b octo-other other &&
+	git merge merge-left merge-right -m "octopus-other" &&
+
+	git checkout other &&
+	git merge octo-other -m "pull octopus" &&
+
+	git checkout second &&
+	git merge octo-second -m "pull octopus" &&
+
+	# Remove these branches so they are not selected
+	# as bitmap tips
+	git branch -D merge-left &&
+	git branch -D merge-right &&
+	git branch -D octo-other &&
+	git branch -D octo-second &&
+
+	# add padding to make these merges less interesting
+	# and avoid having them selected for bitmaps
+	test_commit_bulk --id=file 100 &&
+	git checkout other &&
+	test_commit_bulk --id=side 100 &&
+	git checkout second &&
+
+	bitmaptip=$(git rev-parse second) &&
 	blob=$(echo tagged-blob | git hash-object -w --stdin) &&
 	git tag tagged-blob $blob &&
 	git config repack.writebitmaps true
 '
 
 test_expect_success 'full repack creates bitmaps' '
-	git repack -ad &&
+	GIT_TRACE2_EVENT_NESTING=4 GIT_TRACE2_EVENT="$(pwd)/trace" \
+		git repack -ad &&
 	ls .git/objects/pack/ | grep bitmap >output &&
-	test_line_count = 1 output
+	test_line_count = 1 output &&
+	grep "\"key\":\"num_selected_commits\",\"value\":\"106\"" trace &&
+	grep "\"key\":\"num_maximal_commits\",\"value\":\"107\"" trace
 '
 
 test_expect_success 'rev-list --test-bitmap verifies bitmaps' '
 	git rev-list --test-bitmap HEAD
 '
 
-rev_list_tests() {
-	state=$1
-
-	test_expect_success "counting commits via bitmap ($state)" '
-		git rev-list --count HEAD >expect &&
-		git rev-list --use-bitmap-index --count HEAD >actual &&
+rev_list_tests_head () {
+	test_expect_success "counting commits via bitmap ($state, $branch)" '
+		git rev-list --count $branch >expect &&
+		git rev-list --use-bitmap-index --count $branch >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "counting partial commits via bitmap ($state)" '
-		git rev-list --count HEAD~5..HEAD >expect &&
-		git rev-list --use-bitmap-index --count HEAD~5..HEAD >actual &&
+	test_expect_success "counting partial commits via bitmap ($state, $branch)" '
+		git rev-list --count $branch~5..$branch >expect &&
+		git rev-list --use-bitmap-index --count $branch~5..$branch >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "counting commits with limit ($state)" '
-		git rev-list --count -n 1 HEAD >expect &&
-		git rev-list --use-bitmap-index --count -n 1 HEAD >actual &&
+	test_expect_success "counting commits with limit ($state, $branch)" '
+		git rev-list --count -n 1 $branch >expect &&
+		git rev-list --use-bitmap-index --count -n 1 $branch >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "counting non-linear history ($state)" '
-		git rev-list --count other...master >expect &&
-		git rev-list --use-bitmap-index --count other...master >actual &&
+	test_expect_success "counting non-linear history ($state, $branch)" '
+		git rev-list --count other...second >expect &&
+		git rev-list --use-bitmap-index --count other...second >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "counting commits with limiting ($state)" '
-		git rev-list --count HEAD -- 1.t >expect &&
-		git rev-list --use-bitmap-index --count HEAD -- 1.t >actual &&
+	test_expect_success "counting commits with limiting ($state, $branch)" '
+		git rev-list --count $branch -- 1.t >expect &&
+		git rev-list --use-bitmap-index --count $branch -- 1.t >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "counting objects via bitmap ($state)" '
-		git rev-list --count --objects HEAD >expect &&
-		git rev-list --use-bitmap-index --count --objects HEAD >actual &&
+	test_expect_success "counting objects via bitmap ($state, $branch)" '
+		git rev-list --count --objects $branch >expect &&
+		git rev-list --use-bitmap-index --count --objects $branch >actual &&
 		test_cmp expect actual
 	'
 
-	test_expect_success "enumerate commits ($state)" '
-		git rev-list --use-bitmap-index HEAD >actual &&
-		git rev-list HEAD >expect &&
+	test_expect_success "enumerate commits ($state, $branch)" '
+		git rev-list --use-bitmap-index $branch >actual &&
+		git rev-list $branch >expect &&
 		test_bitmap_traversal --no-confirm-bitmaps expect actual
 	'
 
-	test_expect_success "enumerate --objects ($state)" '
-		git rev-list --objects --use-bitmap-index HEAD >actual &&
-		git rev-list --objects HEAD >expect &&
+	test_expect_success "enumerate --objects ($state, $branch)" '
+		git rev-list --objects --use-bitmap-index $branch >actual &&
+		git rev-list --objects $branch >expect &&
 		test_bitmap_traversal expect actual
 	'
 
-	test_expect_success "bitmap --objects handles non-commit objects ($state)" '
-		git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
+	test_expect_success "bitmap --objects handles non-commit objects ($state, $branch)" '
+		git rev-list --objects --use-bitmap-index $branch tagged-blob >actual &&
 		grep $blob actual
 	'
 }
 
+rev_list_tests () {
+	state=$1
+
+	for branch in "second" "other"
+	do
+		rev_list_tests_head
+	done
+}
+
 rev_list_tests 'full bitmap'
 
 test_expect_success 'clone from bitmapped repository' '
@@ -128,7 +221,7 @@
 rev_list_tests 'partial bitmap'
 
 test_expect_success 'fetch (partial bitmap)' '
-	git --git-dir=clone.git fetch origin master:master &&
+	git --git-dir=clone.git fetch origin second:second &&
 	git rev-parse HEAD >expect &&
 	git --git-dir=clone.git rev-parse HEAD >actual &&
 	test_cmp expect actual
@@ -230,7 +323,7 @@
 '
 
 test_expect_success 'fetch (full bitmap)' '
-	git --git-dir=clone.git fetch origin master:master &&
+	git --git-dir=clone.git fetch origin second:second &&
 	git rev-parse HEAD >expect &&
 	git --git-dir=clone.git rev-parse HEAD >actual &&
 	test_cmp expect actual
@@ -343,7 +436,20 @@
 	test_must_be_empty actual
 '
 
-test_expect_success 'truncated bitmap fails gracefully' '
+test_expect_success 'truncated bitmap fails gracefully (ewah)' '
+	test_config pack.writebitmaphashcache false &&
+	git repack -ad &&
+	git rev-list --use-bitmap-index --count --all >expect &&
+	bitmap=$(ls .git/objects/pack/*.bitmap) &&
+	test_when_finished "rm -f $bitmap" &&
+	test_copy_bytes 256 <$bitmap >$bitmap.tmp &&
+	mv -f $bitmap.tmp $bitmap &&
+	git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+	test_cmp expect actual &&
+	test_i18ngrep corrupt.ewah.bitmap stderr
+'
+
+test_expect_success 'truncated bitmap fails gracefully (cache)' '
 	git repack -ad &&
 	git rev-list --use-bitmap-index --count --all >expect &&
 	bitmap=$(ls .git/objects/pack/*.bitmap) &&
@@ -352,7 +458,7 @@
 	mv -f $bitmap.tmp $bitmap &&
 	git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
 	test_cmp expect actual &&
-	test_i18ngrep corrupt stderr
+	test_i18ngrep corrupted.bitmap.index stderr
 '
 
 # have_delta <obj> <expected_base>
diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh
index da9d599..11423b3 100755
--- a/t/t5312-prune-corruption.sh
+++ b/t/t5312-prune-corruption.sh
@@ -8,6 +8,9 @@
 OK to proceed as if it did not exist. Otherwise we might silently
 delete objects that cannot be recovered.
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'disable reflogs' '
@@ -16,7 +19,7 @@
 '
 
 test_expect_success 'create history reachable only from a bogus-named ref' '
-	test_tick && git commit --allow-empty -m master &&
+	test_tick && git commit --allow-empty -m main &&
 	base=$(git rev-parse HEAD) &&
 	test_tick && git commit --allow-empty -m bogus &&
 	bogus=$(git rev-parse HEAD) &&
@@ -51,7 +54,7 @@
 '
 
 # We create two new objects here, "one" and "two". Our
-# master branch points to "two", which is deleted,
+# main branch points to "two", which is deleted,
 # corrupting the repository. But we'd like to make sure
 # that the otherwise unreachable "one" is not pruned
 # (since it is the user's best bet for recovering
@@ -81,7 +84,7 @@
 test_expect_success 'pack-refs does not silently delete broken loose ref' '
 	git pack-refs --all --prune &&
 	echo $missing >expect &&
-	git rev-parse refs/heads/master >actual &&
+	git rev-parse refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -89,25 +92,25 @@
 # actually pack it, as it is perfectly reasonable to
 # skip processing a broken ref
 test_expect_success 'create packed-refs file with broken ref' '
-	rm -f .git/refs/heads/master &&
+	rm -f .git/refs/heads/main &&
 	cat >.git/packed-refs <<-EOF &&
-	$missing refs/heads/master
+	$missing refs/heads/main
 	$recoverable refs/heads/other
 	EOF
 	echo $missing >expect &&
-	git rev-parse refs/heads/master >actual &&
+	git rev-parse refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'pack-refs does not silently delete broken packed ref' '
 	git pack-refs --all --prune &&
-	git rev-parse refs/heads/master >actual &&
+	git rev-parse refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'pack-refs does not drop broken refs during deletion' '
 	git update-ref -d refs/heads/other &&
-	git rev-parse refs/heads/master >actual &&
+	git rev-parse refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index 0f06c40..a8c1bc0 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -84,14 +84,14 @@
 	pack=$(git pack-objects --all --window=0 </dev/null pack) &&
 	echo 9 >expect &&
 	max_chain pack-$pack.pack >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success '--depth limits depth' '
 	pack=$(git pack-objects --all --depth=5 </dev/null pack) &&
 	echo 5 >expect &&
 	max_chain pack-$pack.pack >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index dc04465..13ed3eb 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -2,6 +2,9 @@
 
 test_description='git pack-objects using object filtering'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Test blob:none filter.
@@ -382,7 +385,7 @@
 	awk -f print_2.awk ls_files_result |
 	sort >expected &&
 
-	git -C r4 pack-objects --revs --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
+	git -C r4 pack-objects --revs --stdout --filter=sparse:oid=main:pattern >filter.pack <<-EOF &&
 	HEAD
 	EOF
 	git -C r4 index-pack ../filter.pack &&
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 2ed0c15..edeb6d6 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -76,7 +76,7 @@
 graph_read_expect() {
 	OPTIONAL=""
 	NUM_CHUNKS=3
-	if test ! -z $2
+	if test ! -z "$2"
 	then
 		OPTIONAL=" $2"
 		NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
@@ -103,14 +103,14 @@
 	# valid commit and tree OID
 	git rev-parse HEAD HEAD^{tree} >in &&
 	git commit-graph write --stdin-commits <in &&
-	graph_read_expect 3
+	graph_read_expect 3 generation_data
 '
 
 test_expect_success 'write graph' '
 	cd "$TRASH_DIRECTORY/full" &&
 	git commit-graph write &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "3"
+	graph_read_expect "3" generation_data
 '
 
 test_expect_success POSIXPERM 'write graph has correct permissions' '
@@ -219,7 +219,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	git commit-graph write &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "10" "extra_edges"
+	graph_read_expect "10" "generation_data extra_edges"
 '
 
 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -254,7 +254,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	git commit-graph write &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "extra_edges"
+	graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -264,7 +264,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	git commit-graph write &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "extra_edges"
+	graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -274,7 +274,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	cat new-idx | git commit-graph write --stdin-packs &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "9" "extra_edges"
+	graph_read_expect "9" "generation_data extra_edges"
 '
 
 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
@@ -287,7 +287,7 @@
 	git rev-parse merge/1 >>commits-in &&
 	cat commits-in | git commit-graph write --stdin-commits &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "6"
+	graph_read_expect "6" "generation_data"
 '
 
 graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
@@ -297,7 +297,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "10" "extra_edges"
+	graph_read_expect "10" "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -307,7 +307,7 @@
 	cd "$TRASH_DIRECTORY/full" &&
 	git commit-graph write --reachable &&
 	test_path_is_file $objdir/info/commit-graph &&
-	graph_read_expect "11" "extra_edges"
+	graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -328,7 +328,7 @@
 	cd "$TRASH_DIRECTORY/bare" &&
 	git commit-graph write &&
 	test_path_is_file $baredir/info/commit-graph &&
-	graph_read_expect "11" "extra_edges"
+	graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
@@ -446,6 +446,27 @@
 	)
 '
 
+test_expect_success 'lower layers have overflow chunk' '
+	cd "$TRASH_DIRECTORY/full" &&
+	UNIX_EPOCH_ZERO="@0 +0000" &&
+	FUTURE_DATE="@2147483646 +0000" &&
+	rm -f .git/objects/info/commit-graph &&
+	test_commit --date "$FUTURE_DATE" future-1 &&
+	test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
+	git commit-graph write --reachable &&
+	test_commit --date "$FUTURE_DATE" future-2 &&
+	test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
+	git commit-graph write --reachable --split=no-merge &&
+	test_commit extra &&
+	git commit-graph write --reachable --split=no-merge &&
+	git commit-graph write --reachable &&
+	graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
+	mv .git/objects/info/commit-graph commit-graph-upgraded &&
+	git commit-graph write --reachable &&
+	graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
+	test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
@@ -454,8 +475,9 @@
 
 test_expect_success 'git commit-graph verify' '
 	cd "$TRASH_DIRECTORY/full" &&
-	git rev-parse commits/8 | git commit-graph write --stdin-commits &&
-	git commit-graph verify >output
+	git rev-parse commits/8 | GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --stdin-commits &&
+	git commit-graph verify >output &&
+	graph_read_expect 9 extra_edges
 '
 
 NUM_COMMITS=9
@@ -529,7 +551,7 @@
 	zero_pos=${4:-${orig_size}} &&
 	printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
 	dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
-	generate_zero_bytes $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
+	test-tool genzeros $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
 	corrupt_graph_verify "$grepstr"
 
 }
@@ -563,7 +585,7 @@
 
 test_expect_success 'detect low chunk count' '
 	corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\01" \
-		"missing the .* chunk"
+		"final chunk has non-zero id"
 '
 
 test_expect_success 'detect missing OID fanout chunk' '
@@ -741,4 +763,56 @@
 	)
 '
 
+# We test the overflow-related code with the following repo history:
+#
+#               4:F - 5:N - 6:U
+#              /                \
+# 1:U - 2:N - 3:U                M:N
+#              \                /
+#               7:N - 8:F - 9:N
+#
+# Here the commits denoted by U have committer date of zero seconds
+# since Unix epoch, the commits denoted by N have committer date
+# starting from 1112354055 seconds since Unix epoch (default committer
+# date for the test suite), and the commits denoted by F have committer
+# date of (2 ^ 31 - 2) seconds since Unix epoch.
+#
+# The largest offset observed is 2 ^ 31, just large enough to overflow.
+#
+
+test_expect_success 'set up and verify repo with generation data overflow chunk' '
+	objdir=".git/objects" &&
+	UNIX_EPOCH_ZERO="@0 +0000" &&
+	FUTURE_DATE="@2147483646 +0000" &&
+	test_oid_cache <<-EOF &&
+	oid_version sha1:1
+	oid_version sha256:2
+	EOF
+	cd "$TRASH_DIRECTORY" &&
+	mkdir repo &&
+	cd repo &&
+	git init &&
+	test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+	test_commit 2 &&
+	test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+	git commit-graph write --reachable &&
+	graph_read_expect 3 generation_data &&
+	test_commit --date "$FUTURE_DATE" 4 &&
+	test_commit 5 &&
+	test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+	git branch left &&
+	git reset --hard 3 &&
+	test_commit 7 &&
+	test_commit --date "$FUTURE_DATE" 8 &&
+	test_commit 9 &&
+	git branch right &&
+	git reset --hard 3 &&
+	test_merge M left right &&
+	git commit-graph write --reachable &&
+	graph_read_expect 10 "generation_data generation_data_overflow" &&
+	git commit-graph verify
+'
+
+graph_git_behavior 'generation data overflow chunk repo' repo left right
+
 test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 297de50..b4afab1 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -314,12 +314,12 @@
 
 test_expect_success 'verify truncated chunk count' '
 	corrupt_midx_and_verify $MIDX_BYTE_CHUNK_COUNT "\01" $objdir \
-		"missing required"
+		"final chunk has non-zero id"
 '
 
 test_expect_success 'verify extended chunk count' '
 	corrupt_midx_and_verify $MIDX_BYTE_CHUNK_COUNT "\07" $objdir \
-		"terminating multi-pack-index chunk id appears earlier than expected"
+		"terminating chunk id appears earlier than expected"
 '
 
 test_expect_success 'verify missing required chunk' '
@@ -329,7 +329,7 @@
 
 test_expect_success 'verify invalid chunk offset' '
 	corrupt_midx_and_verify $MIDX_BYTE_CHUNK_OFFSET "\01" $objdir \
-		"invalid chunk offset (too large)"
+		"improper chunk offset(s)"
 '
 
 test_expect_success 'verify packnames out of order' '
@@ -710,8 +710,9 @@
 		PACKA=$(ls .git/objects/pack/a-pack*\.pack | sed s/\.pack\$//) &&
 		touch $PACKA.keep &&
 		git multi-pack-index expire &&
-		ls -S .git/objects/pack/a-pack* | grep $PACKA >a-pack-files &&
-		test_line_count = 3 a-pack-files &&
+		test_path_is_file $PACKA.idx &&
+		test_path_is_file $PACKA.keep &&
+		test_path_is_file $PACKA.pack &&
 		test-tool read-midx .git/objects | grep idx >midx-list &&
 		test_line_count = 2 midx-list
 	)
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index a581eaf..61cb907 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='pack-objects object selection using sparse algorithm'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
@@ -18,7 +21,7 @@
 	git commit -m "Initialized trees" &&
 	for i in $(test_seq 1 3)
 	do
-		git checkout -b topic$i master &&
+		git checkout -b topic$i main &&
 		echo change-$i >f$i/f$i/data.txt &&
 		git commit -a -m "Changed f$i/f$i/data.txt"
 	done &&
diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
index 7e33408..8b01793 100755
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@ -6,7 +6,7 @@
 test_description='Test git pack-redundant
 
 In order to test git-pack-redundant, we will create a number of objects and
-packs in the repository `master.git`. The relationship between packs (P1-P8)
+packs in the repository `main.git`. The relationship between packs (P1-P8)
 and objects (T, A-R) is showed in the following chart. Objects of a pack will
 be marked with letter x, while objects of redundant packs will be marked with
 exclamation point, and redundant pack itself will be marked with asterisk.
@@ -25,7 +25,7 @@
     ALL | x x x x x x x x x x x x x x x x x x x
 
 Another repository `shared.git` has unique objects (X-Z), while other objects
-(marked with letter s) are shared through alt-odb (of `master.git`). The
+(marked with letter s) are shared through alt-odb (of `main.git`). The
 relationship between packs and objects is as follows:
 
 	| T A B C D E F G H I J K L M N O P Q R   X Y Z
@@ -36,9 +36,11 @@
 
 . ./test-lib.sh
 
-master_repo=master.git
+main_repo=main.git
 shared_repo=shared.git
 
+git_pack_redundant='git pack-redundant --i-still-use-this'
+
 # Create commits in <repo> and assign each commit's oid to shell variables
 # given in the arguments (A, B, and C). E.g.:
 #
@@ -69,7 +71,7 @@
 		shift ||
 		return 1
 	done &&
-	git -C "$repo" update-ref refs/heads/master $oid
+	git -C "$repo" update-ref refs/heads/main $oid
 }
 
 # Create pack in <repo> and assign pack id to variable given in the 2nd argument
@@ -107,9 +109,9 @@
 	sort
 }
 
-test_expect_success 'setup master repo' '
-	git init --bare "$master_repo" &&
-	create_commits_in "$master_repo" A B C D E F G H I J K L M N O P Q R
+test_expect_success 'setup main repo' '
+	git init --bare "$main_repo" &&
+	create_commits_in "$main_repo" A B C D E F G H I J K L M N O P Q R
 '
 
 test_expect_success 'master: pack-redundant works with no packfile' '
@@ -118,7 +120,7 @@
 		cat >expect <<-EOF &&
 			fatal: Zero packs found!
 			EOF
-		test_must_fail git pack-redundant --all >actual 2>&1 &&
+		test_must_fail $git_pack_redundant --all >actual 2>&1 &&
 		test_cmp expect actual
 	)
 '
@@ -133,8 +135,8 @@
 #     ALL | x x x x x x x                       x
 #
 #############################################################################
-test_expect_success 'master: pack-redundant works with one packfile' '
-	create_pack_in "$master_repo" P1 <<-EOF &&
+test_expect_success 'main: pack-redundant works with one packfile' '
+	create_pack_in "$main_repo" P1 <<-EOF &&
 		$T
 		$A
 		$B
@@ -145,8 +147,8 @@
 		$R
 		EOF
 	(
-		cd "$master_repo" &&
-		git pack-redundant --all >out &&
+		cd "$main_repo" &&
+		$git_pack_redundant --all >out &&
 		test_must_be_empty out
 	)
 '
@@ -163,8 +165,8 @@
 #     ALL | x x x x x x x x x x x x x x         x
 #
 #############################################################################
-test_expect_success 'master: no redundant for pack 1, 2, 3' '
-	create_pack_in "$master_repo" P2 <<-EOF &&
+test_expect_success 'main: no redundant for pack 1, 2, 3' '
+	create_pack_in "$main_repo" P2 <<-EOF &&
 		$B
 		$C
 		$D
@@ -173,7 +175,7 @@
 		$H
 		$I
 		EOF
-	create_pack_in "$master_repo" P3 <<-EOF &&
+	create_pack_in "$main_repo" P3 <<-EOF &&
 		$F
 		$I
 		$J
@@ -182,8 +184,8 @@
 		$M
 		EOF
 	(
-		cd "$master_repo" &&
-		git pack-redundant --all >out &&
+		cd "$main_repo" &&
+		$git_pack_redundant --all >out &&
 		test_must_be_empty out
 	)
 '
@@ -202,26 +204,26 @@
 #     ALL | x x x x x x x x x x x x x x x x x   x
 #
 #############################################################################
-test_expect_success 'master: one of pack-2/pack-3 is redundant' '
-	create_pack_in "$master_repo" P4 <<-EOF &&
+test_expect_success 'main: one of pack-2/pack-3 is redundant' '
+	create_pack_in "$main_repo" P4 <<-EOF &&
 		$J
 		$K
 		$L
 		$M
 		$P
 		EOF
-	create_pack_in "$master_repo" P5 <<-EOF &&
+	create_pack_in "$main_repo" P5 <<-EOF &&
 		$G
 		$H
 		$N
 		$O
 		EOF
 	(
-		cd "$master_repo" &&
+		cd "$main_repo" &&
 		cat >expect <<-EOF &&
 			P3:$P3
 			EOF
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		format_packfiles <out >actual &&
 		test_cmp expect actual
 	)
@@ -243,24 +245,24 @@
 #     ALL | x x x x x x x x x x x x x x x x x x x
 #
 #############################################################################
-test_expect_success 'master: pack 2, 4, and 6 are redundant' '
-	create_pack_in "$master_repo" P6 <<-EOF &&
+test_expect_success 'main: pack 2, 4, and 6 are redundant' '
+	create_pack_in "$main_repo" P6 <<-EOF &&
 		$N
 		$O
 		$Q
 		EOF
-	create_pack_in "$master_repo" P7 <<-EOF &&
+	create_pack_in "$main_repo" P7 <<-EOF &&
 		$P
 		$Q
 		EOF
 	(
-		cd "$master_repo" &&
+		cd "$main_repo" &&
 		cat >expect <<-EOF &&
 			P2:$P2
 			P4:$P4
 			P6:$P6
 			EOF
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		format_packfiles <out >actual &&
 		test_cmp expect actual
 	)
@@ -283,57 +285,57 @@
 #     ALL | x x x x x x x x x x x x x x x x x x x
 #
 #############################################################################
-test_expect_success 'master: pack-8 (subset of pack-1) is also redundant' '
-	create_pack_in "$master_repo" P8 <<-EOF &&
+test_expect_success 'main: pack-8 (subset of pack-1) is also redundant' '
+	create_pack_in "$main_repo" P8 <<-EOF &&
 		$A
 		EOF
 	(
-		cd "$master_repo" &&
+		cd "$main_repo" &&
 		cat >expect <<-EOF &&
 			P2:$P2
 			P4:$P4
 			P6:$P6
 			P8:$P8
 			EOF
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		format_packfiles <out >actual &&
 		test_cmp expect actual
 	)
 '
 
-test_expect_success 'master: clean loose objects' '
+test_expect_success 'main: clean loose objects' '
 	(
-		cd "$master_repo" &&
+		cd "$main_repo" &&
 		git prune-packed &&
 		find objects -type f | sed -e "/objects\/pack\//d" >out &&
 		test_must_be_empty out
 	)
 '
 
-test_expect_success 'master: remove redundant packs and pass fsck' '
+test_expect_success 'main: remove redundant packs and pass fsck' '
 	(
-		cd "$master_repo" &&
-		git pack-redundant --all | xargs rm &&
+		cd "$main_repo" &&
+		$git_pack_redundant --all | xargs rm &&
 		git fsck &&
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		test_must_be_empty out
 	)
 '
 
 # The following test cases will execute inside `shared.git`, instead of
-# inside `master.git`.
+# inside `main.git`.
 test_expect_success 'setup shared.git' '
-	git clone --mirror "$master_repo" "$shared_repo" &&
+	git clone --mirror "$main_repo" "$shared_repo" &&
 	(
 		cd "$shared_repo" &&
-		printf "../../$master_repo/objects\n" >objects/info/alternates
+		printf "../../$main_repo/objects\n" >objects/info/alternates
 	)
 '
 
 test_expect_success 'shared: all packs are redundant, but no output without --alt-odb' '
 	(
 		cd "$shared_repo" &&
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		test_must_be_empty out
 	)
 '
@@ -341,7 +343,7 @@
 #############################################################################
 # Chart of packs and objects for this test case
 #
-#     ================ master.git ===============
+#     ================= main.git ================
 #         | T A B C D E F G H I J K L M N O P Q R  <----------+
 #     ----+--------------------------------------             |
 #     P1  | x x x x x x x                       x             |
@@ -372,7 +374,7 @@
 			P5:$P5
 			P7:$P7
 			EOF
-		git pack-redundant --all --verbose >out 2>out.err &&
+		$git_pack_redundant --all --verbose >out 2>out.err &&
 		test_must_be_empty out &&
 		grep "pack$" out.err | format_packfiles >actual &&
 		test_cmp expect actual
@@ -385,9 +387,9 @@
 		cat >expect <<-EOF &&
 			fatal: Zero packs found!
 			EOF
-		git pack-redundant --all --alt-odb | xargs rm &&
+		$git_pack_redundant --all --alt-odb | xargs rm &&
 		git fsck &&
-		test_must_fail git pack-redundant --all --alt-odb >actual 2>&1 &&
+		test_must_fail $git_pack_redundant --all --alt-odb >actual 2>&1 &&
 		test_cmp expect actual
 	)
 '
@@ -415,7 +417,7 @@
 test_expect_success 'shared: no redundant without --alt-odb' '
 	(
 		cd "$shared_repo" &&
-		git pack-redundant --all >out &&
+		$git_pack_redundant --all >out &&
 		test_must_be_empty out
 	)
 '
@@ -423,7 +425,7 @@
 #############################################################################
 # Chart of packs and objects for this test case
 #
-#     ================ master.git ===============
+#     ================= main.git ================
 #         | T A B C D E F G H I J K L M N O P Q R  <----------------+
 #     ----+--------------------------------------                   |
 #     P1  | x x x x x x x                       x                   |
@@ -446,7 +448,7 @@
 test_expect_success 'shared: one pack is redundant with --alt-odb' '
 	(
 		cd "$shared_repo" &&
-		git pack-redundant --all --alt-odb >out &&
+		$git_pack_redundant --all --alt-odb >out &&
 		format_packfiles <out >actual &&
 		test_line_count = 1 actual
 	)
@@ -455,7 +457,7 @@
 #############################################################################
 # Chart of packs and objects for this test case
 #
-#     ================ master.git ===============
+#     ================= main.git ================
 #         | T A B C D E F G H I J K L M N O P Q R  <----------------+
 #     ----+--------------------------------------                   |
 #     P1  | x x x x x x x                       x                   |
@@ -483,7 +485,7 @@
 			Px1:$Px1
 			Px2:$Px2
 			EOF
-		git pack-redundant --all --alt-odb >out <<-EOF &&
+		$git_pack_redundant --all --alt-odb >out <<-EOF &&
 			$X
 			$Y
 			$Z
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index 4d3842b..8e90f34 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -13,11 +13,11 @@
 	infodir=".git/objects/info" &&
 	graphdir="$infodir/commit-graphs" &&
 	test_oid_cache <<-EOM
-	shallow sha1:1760
-	shallow sha256:2064
+	shallow sha1:2132
+	shallow sha256:2436
 
-	base sha1:1376
-	base sha256:1496
+	base sha1:1408
+	base sha256:1528
 
 	oid_version sha1:1
 	oid_version sha256:2
@@ -31,9 +31,9 @@
 		NUM_BASE=$2
 	fi
 	cat >expect <<- EOF
-	header: 43475048 1 $(test_oid oid_version) 3 $NUM_BASE
+	header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
 	num_commits: $1
-	chunks: oid_fanout oid_lookup commit_metadata
+	chunks: oid_fanout oid_lookup commit_metadata generation_data
 	EOF
 	test-tool read-graph >output &&
 	test_cmp expect output
@@ -453,4 +453,185 @@
 	git -C dup commit-graph verify
 '
 
+NUM_FIRST_LAYER_COMMITS=64
+NUM_SECOND_LAYER_COMMITS=16
+NUM_THIRD_LAYER_COMMITS=7
+NUM_FOURTH_LAYER_COMMITS=8
+NUM_FIFTH_LAYER_COMMITS=16
+SECOND_LAYER_SEQUENCE_START=$(($NUM_FIRST_LAYER_COMMITS + 1))
+SECOND_LAYER_SEQUENCE_END=$(($SECOND_LAYER_SEQUENCE_START + $NUM_SECOND_LAYER_COMMITS - 1))
+THIRD_LAYER_SEQUENCE_START=$(($SECOND_LAYER_SEQUENCE_END + 1))
+THIRD_LAYER_SEQUENCE_END=$(($THIRD_LAYER_SEQUENCE_START + $NUM_THIRD_LAYER_COMMITS - 1))
+FOURTH_LAYER_SEQUENCE_START=$(($THIRD_LAYER_SEQUENCE_END + 1))
+FOURTH_LAYER_SEQUENCE_END=$(($FOURTH_LAYER_SEQUENCE_START + $NUM_FOURTH_LAYER_COMMITS - 1))
+FIFTH_LAYER_SEQUENCE_START=$(($FOURTH_LAYER_SEQUENCE_END + 1))
+FIFTH_LAYER_SEQUENCE_END=$(($FIFTH_LAYER_SEQUENCE_START + $NUM_FIFTH_LAYER_COMMITS - 1))
+
+# Current split graph chain:
+#
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'setup repo for mixed generation commit-graph-chain' '
+	graphdir=".git/objects/info/commit-graphs" &&
+	test_oid_cache <<-EOF &&
+	oid_version sha1:1
+	oid_version sha256:2
+	EOF
+	git init mixed &&
+	(
+		cd mixed &&
+		git config core.commitGraph true &&
+		git config gc.writeCommitGraph false &&
+		for i in $(test_seq $NUM_FIRST_LAYER_COMMITS)
+		do
+			test_commit $i &&
+			git branch commits/$i || return 1
+		done &&
+		git commit-graph write --reachable --split &&
+		graph_read_expect $NUM_FIRST_LAYER_COMMITS &&
+		test_line_count = 1 $graphdir/commit-graph-chain &&
+		for i in $(test_seq $SECOND_LAYER_SEQUENCE_START $SECOND_LAYER_SEQUENCE_END)
+		do
+			test_commit $i &&
+			git branch commits/$i || return 1
+		done &&
+		GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable --split=no-merge &&
+		test_line_count = 2 $graphdir/commit-graph-chain &&
+		test-tool read-graph >output &&
+		cat >expect <<-EOF &&
+		header: 43475048 1 $(test_oid oid_version) 4 1
+		num_commits: $NUM_SECOND_LAYER_COMMITS
+		chunks: oid_fanout oid_lookup commit_metadata
+		EOF
+		test_cmp expect output &&
+		git commit-graph verify &&
+		cat $graphdir/commit-graph-chain
+	)
+'
+
+# The new layer will be added without generation data chunk as it was not
+# present on the layer underneath it.
+#
+#      7 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'do not write generation data chunk if not present on existing tip' '
+	git clone mixed mixed-no-gdat &&
+	(
+		cd mixed-no-gdat &&
+		for i in $(test_seq $THIRD_LAYER_SEQUENCE_START $THIRD_LAYER_SEQUENCE_END)
+		do
+			test_commit $i &&
+			git branch commits/$i || return 1
+		done &&
+		git commit-graph write --reachable --split=no-merge &&
+		test_line_count = 3 $graphdir/commit-graph-chain &&
+		test-tool read-graph >output &&
+		cat >expect <<-EOF &&
+		header: 43475048 1 $(test_oid oid_version) 4 2
+		num_commits: $NUM_THIRD_LAYER_COMMITS
+		chunks: oid_fanout oid_lookup commit_metadata
+		EOF
+		test_cmp expect output &&
+		git commit-graph verify
+	)
+'
+
+# Number of commits in each layer of the split-commit graph before merge:
+#
+#      8 commits (No GDAT)
+# ------------------------
+#      7 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+# The top two layers are merged and do not have generation data chunk as layer below them does
+# not have generation data chunk.
+#
+#     15 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'do not write generation data chunk if the topmost remaining layer does not have generation data chunk' '
+	git clone mixed-no-gdat mixed-merge-no-gdat &&
+	(
+		cd mixed-merge-no-gdat &&
+		for i in $(test_seq $FOURTH_LAYER_SEQUENCE_START $FOURTH_LAYER_SEQUENCE_END)
+		do
+			test_commit $i &&
+			git branch commits/$i || return 1
+		done &&
+		git commit-graph write --reachable --split --size-multiple 1 &&
+		test_line_count = 3 $graphdir/commit-graph-chain &&
+		test-tool read-graph >output &&
+		cat >expect <<-EOF &&
+		header: 43475048 1 $(test_oid oid_version) 4 2
+		num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
+		chunks: oid_fanout oid_lookup commit_metadata
+		EOF
+		test_cmp expect output &&
+		git commit-graph verify
+	)
+'
+
+# Number of commits in each layer of the split-commit graph before merge:
+#
+#     16 commits (No GDAT)
+# ------------------------
+#     15 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+# The top three layers are merged and has generation data chunk as the topmost remaining layer
+# has generation data chunk.
+#
+#     47 commits (GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'write generation data chunk if topmost remaining layer has generation data chunk' '
+	git clone mixed-merge-no-gdat mixed-merge-gdat &&
+	(
+		cd mixed-merge-gdat &&
+		for i in $(test_seq $FIFTH_LAYER_SEQUENCE_START $FIFTH_LAYER_SEQUENCE_END)
+		do
+			test_commit $i &&
+			git branch commits/$i || return 1
+		done &&
+		git commit-graph write --reachable --split --size-multiple 1 &&
+		test_line_count = 2 $graphdir/commit-graph-chain &&
+		test-tool read-graph >output &&
+		cat >expect <<-EOF &&
+		header: 43475048 1 $(test_oid oid_version) 5 1
+		num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
+		chunks: oid_fanout oid_lookup commit_metadata generation_data
+		EOF
+		test_cmp expect output
+	)
+'
+
+test_expect_success 'write generation data chunk when commit-graph chain is replaced' '
+	git clone mixed mixed-replace &&
+	(
+		cd mixed-replace &&
+		git commit-graph write --reachable --split=replace &&
+		test_path_is_file $graphdir/commit-graph-chain &&
+		test_line_count = 1 $graphdir/commit-graph-chain &&
+		verify_chain_files_exist $graphdir &&
+		graph_read_expect $(($NUM_FIRST_LAYER_COMMITS + $NUM_SECOND_LAYER_COMMITS)) &&
+		git commit-graph verify
+	)
+'
+
 test_done
diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh
new file mode 100755
index 0000000..da453f6
--- /dev/null
+++ b/t/t5325-reverse-index.sh
@@ -0,0 +1,120 @@
+#!/bin/sh
+
+test_description='on-disk reverse index'
+. ./test-lib.sh
+
+# The below tests want control over the 'pack.writeReverseIndex' setting
+# themselves to assert various combinations of it with other options.
+sane_unset GIT_TEST_WRITE_REV_INDEX
+
+packdir=.git/objects/pack
+
+test_expect_success 'setup' '
+	test_commit base &&
+
+	pack=$(git pack-objects --all $packdir/pack) &&
+	rev=$packdir/pack-$pack.rev &&
+
+	test_path_is_missing $rev
+'
+
+test_index_pack () {
+	rm -f $rev &&
+	conf=$1 &&
+	shift &&
+	# remove the index since Windows won't overwrite an existing file
+	rm $packdir/pack-$pack.idx &&
+	git -c pack.writeReverseIndex=$conf index-pack "$@" \
+		$packdir/pack-$pack.pack
+}
+
+test_expect_success 'index-pack with pack.writeReverseIndex' '
+	test_index_pack "" &&
+	test_path_is_missing $rev &&
+
+	test_index_pack false &&
+	test_path_is_missing $rev &&
+
+	test_index_pack true &&
+	test_path_is_file $rev
+'
+
+test_expect_success 'index-pack with --[no-]rev-index' '
+	for conf in "" true false
+	do
+		test_index_pack "$conf" --rev-index &&
+		test_path_exists $rev &&
+
+		test_index_pack "$conf" --no-rev-index &&
+		test_path_is_missing $rev
+	done
+'
+
+test_expect_success 'index-pack can verify reverse indexes' '
+	test_when_finished "rm -f $rev" &&
+	test_index_pack true &&
+
+	test_path_is_file $rev &&
+	git index-pack --rev-index --verify $packdir/pack-$pack.pack &&
+
+	# Intentionally corrupt the reverse index.
+	chmod u+w $rev &&
+	printf "xxxx" | dd of=$rev bs=1 count=4 conv=notrunc &&
+
+	test_must_fail git index-pack --rev-index --verify \
+		$packdir/pack-$pack.pack 2>err &&
+	grep "validation error" err
+'
+
+test_expect_success 'index-pack infers reverse index name with -o' '
+	git index-pack --rev-index -o other.idx $packdir/pack-$pack.pack &&
+	test_path_is_file other.idx &&
+	test_path_is_file other.rev
+'
+
+test_expect_success 'pack-objects respects pack.writeReverseIndex' '
+	test_when_finished "rm -fr pack-1-*" &&
+
+	git -c pack.writeReverseIndex= pack-objects --all pack-1 &&
+	test_path_is_missing pack-1-*.rev &&
+
+	git -c pack.writeReverseIndex=false pack-objects --all pack-1 &&
+	test_path_is_missing pack-1-*.rev &&
+
+	git -c pack.writeReverseIndex=true pack-objects --all pack-1 &&
+	test_path_is_file pack-1-*.rev
+'
+
+test_expect_success 'reverse index is not generated when available on disk' '
+	test_index_pack true &&
+	test_path_is_file $rev &&
+
+	git rev-parse HEAD >tip &&
+	GIT_TEST_REV_INDEX_DIE_IN_MEMORY=1 git cat-file \
+		--batch-check="%(objectsize:disk)" <tip
+'
+
+test_expect_success 'revindex in-memory vs on-disk' '
+	git init repo &&
+	test_when_finished "rm -fr repo" &&
+	(
+		cd repo &&
+
+		test_commit commit &&
+
+		git rev-list --objects --no-object-names --all >objects &&
+
+		git -c pack.writeReverseIndex=false repack -ad &&
+		test_path_is_missing $packdir/pack-*.rev &&
+		git cat-file --batch-check="%(objectsize:disk) %(objectname)" \
+			<objects >in-core &&
+
+		git -c pack.writeReverseIndex=true repack -ad &&
+		test_path_is_file $packdir/pack-*.rev &&
+		git cat-file --batch-check="%(objectsize:disk) %(objectname)" \
+			<objects >on-disk &&
+
+		test_cmp on-disk in-core
+	)
+'
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index cc86ef2..3f81f16 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -6,6 +6,9 @@
 test_description='See why rewinding head breaks send-pack
 
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 cnt=64
@@ -66,20 +69,20 @@
 '
 
 test_expect_success 'refuse pushing rewound head without --force' '
-	pushed_head=$(git rev-parse --verify master) &&
-	victim_orig=$(cd victim && git rev-parse --verify master) &&
-	test_must_fail git send-pack ./victim master &&
-	victim_head=$(cd victim && git rev-parse --verify master) &&
+	pushed_head=$(git rev-parse --verify main) &&
+	victim_orig=$(cd victim && git rev-parse --verify main) &&
+	test_must_fail git send-pack ./victim main &&
+	victim_head=$(cd victim && git rev-parse --verify main) &&
 	test "$victim_head" = "$victim_orig" &&
 	# this should update
-	git send-pack --force ./victim master &&
-	victim_head=$(cd victim && git rev-parse --verify master) &&
+	git send-pack --force ./victim main &&
+	victim_head=$(cd victim && git rev-parse --verify main) &&
 	test "$victim_head" = "$pushed_head"
 '
 
 test_expect_success 'push can be used to delete a ref' '
-	( cd victim && git branch extra master ) &&
-	git send-pack ./victim :extra master &&
+	( cd victim && git branch extra main ) &&
+	git send-pack ./victim :extra main &&
 	( cd victim &&
 	  test_must_fail git rev-parse --verify extra )
 '
@@ -89,9 +92,9 @@
 		cd victim &&
 		test_might_fail git branch -D extra &&
 		git config receive.denyDeletes true &&
-		git branch extra master
+		git branch extra main
 	) &&
-	test_must_fail git send-pack ./victim :extra master
+	test_must_fail git send-pack ./victim :extra main
 '
 
 test_expect_success 'cannot override denyDeletes with git -c send-pack' '
@@ -99,10 +102,10 @@
 		cd victim &&
 		test_might_fail git branch -D extra &&
 		git config receive.denyDeletes true &&
-		git branch extra master
+		git branch extra main
 	) &&
 	test_must_fail git -c receive.denyDeletes=false \
-					send-pack ./victim :extra master
+					send-pack ./victim :extra main
 '
 
 test_expect_success 'override denyDeletes with git -c receive-pack' '
@@ -110,11 +113,11 @@
 		cd victim &&
 		test_might_fail git branch -D extra &&
 		git config receive.denyDeletes true &&
-		git branch extra master
+		git branch extra main
 	) &&
 	git send-pack \
 		--receive-pack="git -c receive.denyDeletes=false receive-pack" \
-		./victim :extra master
+		./victim :extra main
 '
 
 test_expect_success 'denyNonFastforwards trumps --force' '
@@ -123,9 +126,9 @@
 		test_might_fail git branch -D extra &&
 		git config receive.denyNonFastforwards true
 	) &&
-	victim_orig=$(cd victim && git rev-parse --verify master) &&
-	test_must_fail git send-pack --force ./victim master^:master &&
-	victim_head=$(cd victim && git rev-parse --verify master) &&
+	victim_orig=$(cd victim && git rev-parse --verify main) &&
+	test_must_fail git send-pack --force ./victim main^:main &&
+	victim_head=$(cd victim && git rev-parse --verify main) &&
 	test "$victim_orig" = "$victim_head"
 '
 
@@ -210,41 +213,41 @@
 
 test_expect_success 'pushing explicit refspecs respects forcing' '
 	rewound_push_setup &&
-	parent_orig=$(cd parent && git rev-parse --verify master) &&
+	parent_orig=$(cd parent && git rev-parse --verify main) &&
 	(
 		cd child &&
 		test_must_fail git send-pack ../parent \
-			refs/heads/master:refs/heads/master
+			refs/heads/main:refs/heads/main
 	) &&
-	parent_head=$(cd parent && git rev-parse --verify master) &&
+	parent_head=$(cd parent && git rev-parse --verify main) &&
 	test "$parent_orig" = "$parent_head" &&
 	(
 		cd child &&
 		git send-pack ../parent \
-			+refs/heads/master:refs/heads/master
+			+refs/heads/main:refs/heads/main
 	) &&
-	parent_head=$(cd parent && git rev-parse --verify master) &&
-	child_head=$(cd child && git rev-parse --verify master) &&
+	parent_head=$(cd parent && git rev-parse --verify main) &&
+	child_head=$(cd child && git rev-parse --verify main) &&
 	test "$parent_head" = "$child_head"
 '
 
 test_expect_success 'pushing wildcard refspecs respects forcing' '
 	rewound_push_setup &&
-	parent_orig=$(cd parent && git rev-parse --verify master) &&
+	parent_orig=$(cd parent && git rev-parse --verify main) &&
 	(
 		cd child &&
 		test_must_fail git send-pack ../parent \
 			"refs/heads/*:refs/heads/*"
 	) &&
-	parent_head=$(cd parent && git rev-parse --verify master) &&
+	parent_head=$(cd parent && git rev-parse --verify main) &&
 	test "$parent_orig" = "$parent_head" &&
 	(
 		cd child &&
 		git send-pack ../parent \
 			"+refs/heads/*:refs/heads/*"
 	) &&
-	parent_head=$(cd parent && git rev-parse --verify master) &&
-	child_head=$(cd child && git rev-parse --verify master) &&
+	parent_head=$(cd parent && git rev-parse --verify main) &&
+	child_head=$(cd child && git rev-parse --verify main) &&
 	test "$parent_head" = "$child_head"
 '
 
@@ -252,7 +255,7 @@
 	rewound_push_setup &&
 	(
 		cd child &&
-		test_must_fail git send-pack ../parent :refs/heads/master 2>errs
+		test_must_fail git send-pack ../parent :refs/heads/main 2>errs
 	)
 '
 
@@ -283,9 +286,9 @@
 	local=$(git -C fork rev-parse HEAD) &&
 	shared=$(git -C shared rev-parse only-shared) &&
 	cat >expect <<-EOF &&
-	$local refs/heads/master
+	$local refs/heads/main
 	$local refs/remotes/origin/HEAD
-	$local refs/remotes/origin/master
+	$local refs/remotes/origin/main
 	$shared .have
 	EOF
 
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
index 956d69f..6012cc8 100755
--- a/t/t5401-update-hooks.sh
+++ b/t/t5401-update-hooks.sh
@@ -15,11 +15,11 @@
 	git update-index a &&
 	tree1=$(git write-tree) &&
 	commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
-	git update-ref refs/heads/master $commit0 &&
+	git update-ref refs/heads/main $commit0 &&
 	git update-ref refs/heads/tofail $commit1 &&
 	git clone --bare ./. victim.git &&
 	GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
-	git update-ref refs/heads/master $commit1 &&
+	git update-ref refs/heads/main $commit1 &&
 	git update-ref refs/heads/tofail $commit0
 '
 
@@ -38,7 +38,7 @@
 read x; printf %s "$x" >$GIT_DIR/update.stdin
 echo STDOUT update $1
 echo STDERR update $1 >&2
-test "$1" = refs/heads/master || exit
+test "$1" = refs/heads/main || exit
 EOF
 chmod u+x victim.git/hooks/update
 
@@ -62,11 +62,11 @@
 
 test_expect_success push '
 	test_must_fail git send-pack --force ./victim.git \
-		master tofail >send.out 2>send.err
+		main tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
-	test $(GIT_DIR=victim.git git rev-parse master) = $commit1 &&
+	test $(GIT_DIR=victim.git git rev-parse main) = $commit1 &&
 	test $(GIT_DIR=victim.git git rev-parse tofail) = $commit1
 '
 
@@ -82,24 +82,24 @@
 '
 
 test_expect_success 'pre-receive hook input' '
-	(echo $commit0 $commit1 refs/heads/master &&
+	(echo $commit0 $commit1 refs/heads/main &&
 	 echo $commit1 $commit0 refs/heads/tofail
 	) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
-	(echo refs/heads/master $commit0 $commit1 &&
+	(echo refs/heads/main $commit0 $commit1 &&
 	 echo refs/heads/tofail $commit1 $commit0
 	) | test_cmp - victim.git/update.args
 '
 
 test_expect_success 'post-receive hook input' '
-	echo $commit0 $commit1 refs/heads/master |
+	echo $commit0 $commit1 refs/heads/main |
 	test_cmp - victim.git/post-receive.stdin
 '
 
 test_expect_success 'post-update hook arguments' '
-	echo refs/heads/master |
+	echo refs/heads/main |
 	test_cmp - victim.git/post-update.args
 '
 
@@ -120,8 +120,8 @@
 cat <<EOF >expect
 remote: STDOUT pre-receive
 remote: STDERR pre-receive
-remote: STDOUT update refs/heads/master
-remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/main
+remote: STDERR update refs/heads/main
 remote: STDOUT update refs/heads/tofail
 remote: STDERR update refs/heads/tofail
 remote: error: hook declined to update refs/heads/tofail
@@ -143,7 +143,7 @@
 
 	for v in $(test_seq 100 999)
 	do
-		git branch branch_$v master || return
+		git branch branch_$v main || return
 	done &&
 	git push ./victim.git "+refs/heads/*:refs/heads/*"
 '
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
index 4aeea8f..3e5e19c 100755
--- a/t/t5402-post-merge-hook.sh
+++ b/t/t5402-post-merge-hook.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test the post-merge hook.'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -15,7 +18,7 @@
 	git update-index a &&
 	tree1=$(git write-tree) &&
 	commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
-	git update-ref refs/heads/master $commit0 &&
+	git update-ref refs/heads/main $commit0 &&
 	git clone ./. clone1 &&
 	GIT_DIR=clone1/.git git update-index --add a &&
 	git clone ./. clone2 &&
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
index a39b3b5..1ec9e23 100755
--- a/t/t5403-post-checkout-hook.sh
+++ b/t/t5403-post-checkout-hook.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test the post-checkout hook.'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -20,7 +23,7 @@
 
 test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
 	test_when_finished "rm -f .git/post-checkout.args" &&
-	git checkout master &&
+	git checkout main &&
 	read old new flag <.git/post-checkout.args &&
 	test $old = $new && test $flag = 1
 '
@@ -41,14 +44,14 @@
 
 test_expect_success 'post-checkout receives the right args when not switching branches ' '
 	test_when_finished "rm -f .git/post-checkout.args" &&
-	git checkout master -- three.t &&
+	git checkout main -- three.t &&
 	read old new flag <.git/post-checkout.args &&
 	test $old = $new && test $flag = 0
 '
 
 test_expect_success 'post-checkout is triggered on rebase' '
 	test_when_finished "rm -f .git/post-checkout.args" &&
-	git checkout -b rebase-test master &&
+	git checkout -b rebase-test main &&
 	rm -f .git/post-checkout.args &&
 	git rebase rebase-on-me &&
 	read old new flag <.git/post-checkout.args &&
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
index 2762f42..cc07889 100755
--- a/t/t5404-tracking-branches.sh
+++ b/t/t5404-tracking-branches.sh
@@ -2,6 +2,9 @@
 
 test_description='tracking branch update checks for git push'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -30,9 +33,9 @@
 	git checkout -b b2 origin/b2 &&
 	echo aa-b2 >>file &&
 	git commit -a -m aa-b2 &&
-	git checkout master &&
-	echo aa-master >>file &&
-	git commit -a -m aa-master
+	git checkout main &&
+	echo aa-main >>file &&
+	git commit -a -m aa-main
 '
 
 test_expect_success 'mixed-success push returns error' '
@@ -40,7 +43,7 @@
 '
 
 test_expect_success 'check tracking branches updated correctly after push' '
-	test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+	test "$(git rev-parse origin/main)" = "$(git rev-parse main)"
 '
 
 test_expect_success 'check tracking branches not updated for failed refs' '
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
index 235fb76..11f0323 100755
--- a/t/t5405-send-pack-rewind.sh
+++ b/t/t5405-send-pack-rewind.sh
@@ -2,6 +2,9 @@
 
 test_description='forced push to replace commit we do not have'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -13,7 +16,7 @@
 	mkdir another && (
 		cd another &&
 		git init &&
-		git fetch --update-head-ok .. master:master
+		git fetch --update-head-ok .. main:main
 	) &&
 
 	>file2 && git add file2 && test_tick &&
@@ -25,7 +28,7 @@
 
 	(
 		cd another &&
-		test_must_fail git push .. master:master
+		test_must_fail git push .. main:main
 	)
 
 '
@@ -34,7 +37,7 @@
 
 	(
 		cd another &&
-		git push .. +master:master
+		git push .. +main:main
 	)
 
 '
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 80750a8..5bb23cc 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test the post-rewrite hook.'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -14,7 +17,7 @@
 	git checkout A^0 &&
 	test_commit E bar E &&
 	test_commit F foo F &&
-	git checkout master
+	git checkout main
 '
 
 mkdir .git/hooks
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
index f00d0da..0b28e4e 100755
--- a/t/t5410-receive-pack-alternates.sh
+++ b/t/t5410-receive-pack-alternates.sh
@@ -2,14 +2,17 @@
 
 test_description='git receive-pack with alternate ref filtering'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
 	test_commit base &&
 	git clone -s --bare . fork &&
-	git checkout -b public/branch master &&
+	git checkout -b public/branch main &&
 	test_commit public &&
-	git checkout -b private/branch master &&
+	git checkout -b private/branch main &&
 	test_commit private
 '
 
diff --git a/t/t5411/common-functions.sh b/t/t5411/common-functions.sh
index 344d13f..6694858 100644
--- a/t/t5411/common-functions.sh
+++ b/t/t5411/common-functions.sh
@@ -36,9 +36,8 @@
 # without having to worry about future changes of the commit ID and spaces
 # of the output.  Single quotes are replaced with double quotes, because
 # it is boring to prepare unquoted single quotes in expect text.  We also
-# remove some locale error messages, which break test if we turn on
-# `GIT_TEST_GETTEXT_POISON=true` in order to test unintentional translations
-# on plumbing commands.
+# remove some locale error messages. The emitted human-readable errors are
+# redundant to the more machine-readable output the tests already assert.
 make_user_friendly_and_stable_output () {
 	sed \
 		-e "s/  *\$//" \
@@ -59,3 +58,18 @@
 	make_user_friendly_and_stable_output |
 		sed -n ${1+"$@"}
 }
+
+test_cmp_refs () {
+	indir=
+	if test "$1" = "-C"
+	then
+		shift
+		indir="$1"
+		shift
+	fi
+	indir=${indir:+"$indir"/}
+	cat >show-ref.expect &&
+	git ${indir:+ -C "$indir"} show-ref >show-ref.pristine &&
+	make_user_friendly_and_stable_output <show-ref.pristine >show-ref.filtered &&
+	test_cmp show-ref.expect show-ref.filtered
+}
diff --git a/t/t5411/once-0010-report-status-v1.sh b/t/t5411/once-0010-report-status-v1.sh
index cb431a9..1233a46 100644
--- a/t/t5411/once-0010-report-status-v1.sh
+++ b/t/t5411/once-0010-report-status-v1.sh
@@ -83,12 +83,9 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/for/main/topic1
 	<COMMIT-A> refs/heads/foo
 	<COMMIT-B> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0000-standard-git-push.sh b/t/t5411/test-0000-standard-git-push.sh
index 47b058a..e1e0175 100644
--- a/t/t5411/test-0000-standard-git-push.sh
+++ b/t/t5411/test-0000-standard-git-push.sh
@@ -19,13 +19,11 @@
 	 * [new branch] HEAD -> next
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -35,24 +33,22 @@
 	test_must_fail git -C workbench push --atomic origin \
 		main \
 		$B:refs/heads/next \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [rejected] main -> main (non-fast-forward)
 	 ! [rejected] <COMMIT-B> -> next (atomic push failed)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -65,8 +61,8 @@
 		push origin \
 		main \
 		$B:refs/heads/next \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
@@ -77,13 +73,11 @@
 	 ! [rejected] main -> main (non-fast-forward)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-B> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(B)
@@ -119,15 +113,13 @@
 	 * [new branch] HEAD -> a/b/c
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/a/b/c
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/review/main/topic
 	<TAG-v123> refs/tags/v123
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)  tags/v123  refs/review/main/topic(A)  a/b/c(A)
diff --git a/t/t5411/test-0001-standard-git-push--porcelain.sh b/t/t5411/test-0001-standard-git-push--porcelain.sh
index bbead12..bcbda72 100644
--- a/t/t5411/test-0001-standard-git-push--porcelain.sh
+++ b/t/t5411/test-0001-standard-git-push--porcelain.sh
@@ -20,13 +20,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -36,25 +34,23 @@
 	test_must_fail git -C workbench push --atomic --porcelain origin \
 		main \
 		$B:refs/heads/next \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "s/^# GETTEXT POISON #//" \
 		-e "/^To / { p; }" \
 		-e "/^! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    refs/heads/main:refs/heads/main    [rejected] (non-fast-forward)
 	!    <COMMIT-B>:refs/heads/next    [rejected] (atomic push failed)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -67,8 +63,8 @@
 		push --porcelain origin \
 		main \
 		$B:refs/heads/next \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
@@ -80,13 +76,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	<COMMIT-B> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(B)
@@ -123,15 +117,13 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/a/b/c
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/review/main/topic
 	<TAG-v123> refs/tags/v123
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)  tags/v123  refs/review/main/topic(A)  a/b/c(A)
diff --git a/t/t5411/test-0002-pre-receive-declined.sh b/t/t5411/test-0002-pre-receive-declined.sh
index e7d113a..0c3490c 100644
--- a/t/t5411/test-0002-pre-receive-declined.sh
+++ b/t/t5411/test-0002-pre-receive-declined.sh
@@ -12,20 +12,18 @@
 	test_must_fail git -C workbench push origin \
 		$B:refs/heads/main \
 		HEAD:refs/heads/next \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] <COMMIT-B> -> main (pre-receive hook declined)
 	 ! [remote rejected] HEAD -> next (pre-receive hook declined)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "cleanup ($PROTOCOL)" '
diff --git a/t/t5411/test-0003-pre-receive-declined--porcelain.sh b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
index cc0cca6..e9c9db5 100644
--- a/t/t5411/test-0003-pre-receive-declined--porcelain.sh
+++ b/t/t5411/test-0003-pre-receive-declined--porcelain.sh
@@ -12,8 +12,8 @@
 	test_must_fail git -C workbench push --porcelain origin \
 		$B:refs/heads/main \
 		HEAD:refs/heads/next \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    <COMMIT-B>:refs/heads/main    [remote rejected] (pre-receive hook declined)
@@ -21,12 +21,10 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "cleanup ($PROTOCOL/porcelain)" '
diff --git a/t/t5411/test-0011-no-hook-error.sh b/t/t5411/test-0011-no-hook-error.sh
index c508309..3ef136e 100644
--- a/t/t5411/test-0011-no-hook-error.sh
+++ b/t/t5411/test-0011-no-hook-error.sh
@@ -5,8 +5,8 @@
 	test_must_fail git -C workbench push origin \
 		HEAD:next \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -19,13 +19,11 @@
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -41,8 +39,8 @@
 	test_must_fail git -C workbench push --atomic origin \
 		$B:main \
 		HEAD:next \
-		HEAD:refs/for/main/topic >out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -55,10 +53,8 @@
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0012-no-hook-error--porcelain.sh b/t/t5411/test-0012-no-hook-error--porcelain.sh
index 14ea433..19f66fb 100644
--- a/t/t5411/test-0012-no-hook-error--porcelain.sh
+++ b/t/t5411/test-0012-no-hook-error--porcelain.sh
@@ -5,8 +5,8 @@
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:next \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -20,13 +20,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -42,8 +40,8 @@
 	test_must_fail git -C workbench push --porcelain --atomic origin \
 		$B:main \
 		HEAD:next \
-		HEAD:refs/for/main/topic >out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -57,10 +55,8 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0013-bad-protocol.sh b/t/t5411/test-0013-bad-protocol.sh
index b9be12b..095e613 100644
--- a/t/t5411/test-0013-bad-protocol.sh
+++ b/t/t5411/test-0013-bad-protocol.sh
@@ -11,8 +11,8 @@
 test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 
 	# Check status report for git-push
 	sed -n \
@@ -34,12 +34,9 @@
 	EOF
 	test_cmp expect actual-error &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL)" '
@@ -55,25 +52,22 @@
 test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-version option" out &&
-	grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+	grep "remote: fatal: die with the --die-read-version option" out-$test_count &&
+	grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL)" '
@@ -89,25 +83,22 @@
 test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-write-version option" out &&
-	grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+	grep "remote: fatal: die with the --die-write-version option" out-$test_count &&
+	grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL)" '
@@ -123,24 +114,21 @@
 test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-commands option" out &&
+	grep "remote: fatal: die with the --die-read-commands option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL)" '
@@ -158,24 +146,21 @@
 	test_must_fail git -C workbench push origin \
 		-o reviewers=user1,user2 \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-push-options option" out &&
+	grep "remote: fatal: die with the --die-read-push-options option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL)" '
@@ -191,24 +176,21 @@
 test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; }" \
 		-e "/^ ! / { p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	 ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-write-report option" out &&
+	grep "remote: fatal: die with the --die-write-report option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (no report, $PROTOCOL)" '
@@ -224,8 +206,8 @@
 test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/heads/next \
-		HEAD:refs/for/main/topic >out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -240,13 +222,10 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -270,8 +249,8 @@
 test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic\
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -284,12 +263,9 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL)" '
@@ -306,8 +282,8 @@
 test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 			HEAD:refs/for/main/topic \
-			>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+			>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -320,10 +296,7 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0014-bad-protocol--porcelain.sh b/t/t5411/test-0014-bad-protocol--porcelain.sh
index fdb4569..a446497 100644
--- a/t/t5411/test-0014-bad-protocol--porcelain.sh
+++ b/t/t5411/test-0014-bad-protocol--porcelain.sh
@@ -11,8 +11,8 @@
 test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 
 	# Check status report for git-push
 	sed -n \
@@ -34,12 +34,9 @@
 	EOF
 	test_cmp expect actual-error &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL/porcelain)" '
@@ -55,25 +52,22 @@
 test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; n; p; n; p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
 	Done
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-version option" out &&
-	grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+	grep "remote: fatal: die with the --die-read-version option" out-$test_count &&
+	grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL/porcelain)" '
@@ -89,25 +83,22 @@
 test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; n; p; n; p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
 	Done
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-write-version option" out &&
-	grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+	grep "remote: fatal: die with the --die-write-version option" out-$test_count &&
+	grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL/porcelain)" '
@@ -123,24 +114,21 @@
 test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; n; p; n; p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
 	Done
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-commands option" out &&
+	grep "remote: fatal: die with the --die-read-commands option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL/porcelain)" '
@@ -158,24 +146,21 @@
 	test_must_fail git -C workbench push --porcelain origin \
 		-o reviewers=user1,user2 \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; n; p; n; p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
 	Done
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-read-push-options option" out &&
+	grep "remote: fatal: die with the --die-read-push-options option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL/porcelain)" '
@@ -191,24 +176,21 @@
 test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
+		>out-$test_count 2>&1 &&
 	filter_out_user_friendly_and_stable_output \
 		-e "/^To / { p; n; p; n; p; }" \
-		<out >actual &&
+		<out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	To <URL/of/upstream.git>
 	!    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
 	Done
 	EOF
 	test_cmp expect actual &&
-	grep "remote: fatal: die with the --die-write-report option" out &&
+	grep "remote: fatal: die with the --die-write-report option" out-$test_count &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
@@ -224,8 +206,8 @@
 test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/heads/next \
-		HEAD:refs/for/main/topic >out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -241,13 +223,10 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -270,8 +249,8 @@
 test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic\
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -285,12 +264,9 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL/porcelain)" '
@@ -307,8 +283,8 @@
 test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 			HEAD:refs/for/main/topic \
-			>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+			>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -322,10 +298,7 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0020-report-ng.sh b/t/t5411/test-0020-report-ng.sh
index 5a9e0da..ad2c8f6 100644
--- a/t/t5411/test-0020-report-ng.sh
+++ b/t/t5411/test-0020-report-ng.sh
@@ -12,8 +12,8 @@
 test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -24,12 +24,10 @@
 	 ! [remote rejected] HEAD -> refs/for/main/topic (failed)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (ng message, $PROTOCOL)" '
@@ -46,8 +44,8 @@
 test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -58,10 +56,8 @@
 	 ! [remote rejected] HEAD -> refs/for/main/topic (error msg)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0021-report-ng--porcelain.sh b/t/t5411/test-0021-report-ng--porcelain.sh
index 93475a8..d8ae9d3 100644
--- a/t/t5411/test-0021-report-ng--porcelain.sh
+++ b/t/t5411/test-0021-report-ng--porcelain.sh
@@ -12,8 +12,8 @@
 test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -25,12 +25,10 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (ng message, $PROTOCOL/porcelain)" '
@@ -47,8 +45,8 @@
 test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -60,10 +58,8 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0022-report-unexpect-ref.sh b/t/t5411/test-0022-report-unexpect-ref.sh
index f8be8a0..dbed467 100644
--- a/t/t5411/test-0022-report-unexpect-ref.sh
+++ b/t/t5411/test-0022-report-unexpect-ref.sh
@@ -13,8 +13,8 @@
 	test_must_fail git -C workbench push origin \
 		$B:refs/heads/main \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -30,12 +30,10 @@
 	 ! [remote rejected] HEAD -> refs/for/main/topic (proc-receive failed to report status)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)
diff --git a/t/t5411/test-0023-report-unexpect-ref--porcelain.sh b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
index 778150f..e89096f 100644
--- a/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
+++ b/t/t5411/test-0023-report-unexpect-ref--porcelain.sh
@@ -13,8 +13,8 @@
 	test_must_fail git -C workbench push --porcelain origin \
 		$B:refs/heads/main \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -31,12 +31,10 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)
diff --git a/t/t5411/test-0024-report-unknown-ref.sh b/t/t5411/test-0024-report-unknown-ref.sh
index d4e74e4..7720424 100644
--- a/t/t5411/test-0024-report-unknown-ref.sh
+++ b/t/t5411/test-0024-report-unknown-ref.sh
@@ -12,8 +12,8 @@
 test_expect_success "proc-receive: report unknown reference ($PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/a/b/c/my/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
@@ -25,10 +25,8 @@
 	 ! [remote rejected] HEAD -> refs/for/a/b/c/my/topic (proc-receive failed to report status)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0025-report-unknown-ref--porcelain.sh b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
index 039e8b6..eeb1ce6 100644
--- a/t/t5411/test-0025-report-unknown-ref--porcelain.sh
+++ b/t/t5411/test-0025-report-unknown-ref--porcelain.sh
@@ -12,8 +12,8 @@
 test_expect_success "proc-receive: report unknown reference ($PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/a/b/c/my/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
@@ -26,10 +26,8 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0026-push-options.sh b/t/t5411/test-0026-push-options.sh
index e88edb1..1ec2cb9 100644
--- a/t/t5411/test-0026-push-options.sh
+++ b/t/t5411/test-0026-push-options.sh
@@ -16,16 +16,14 @@
 		-o reviewer=user1 \
 		origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	test_i18ngrep "fatal: the receiving end does not support push options" \
 		actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "enable push options ($PROTOCOL)" '
@@ -69,13 +67,11 @@
 	 * [new reference] HEAD -> refs/for/main/topic
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "restore proc-receive hook ($PROTOCOL)" '
@@ -123,13 +119,11 @@
 	 * [new reference] HEAD -> refs/for/main/topic
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
diff --git a/t/t5411/test-0027-push-options--porcelain.sh b/t/t5411/test-0027-push-options--porcelain.sh
index 3a6561b..447fbfe 100644
--- a/t/t5411/test-0027-push-options--porcelain.sh
+++ b/t/t5411/test-0027-push-options--porcelain.sh
@@ -17,16 +17,14 @@
 		-o reviewer=user1 \
 		origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	test_i18ngrep "fatal: the receiving end does not support push options" \
 		actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "enable push options ($PROTOCOL/porcelain)" '
@@ -72,13 +70,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "restore proc-receive hook ($PROTOCOL/porcelain)" '
@@ -128,13 +124,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/next
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
diff --git a/t/t5411/test-0030-report-ok.sh b/t/t5411/test-0030-report-ok.sh
index 5d6feef..8acb4f2 100644
--- a/t/t5411/test-0030-report-ok.sh
+++ b/t/t5411/test-0030-report-ok.sh
@@ -26,10 +26,8 @@
 	 * [new reference] HEAD -> refs/for/main/topic
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0031-report-ok--porcelain.sh b/t/t5411/test-0031-report-ok--porcelain.sh
index 91666d3..a967718 100644
--- a/t/t5411/test-0031-report-ok--porcelain.sh
+++ b/t/t5411/test-0031-report-ok--porcelain.sh
@@ -27,10 +27,8 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0032-report-with-options.sh b/t/t5411/test-0032-report-with-options.sh
index a0faf5c..437ade0 100644
--- a/t/t5411/test-0032-report-with-options.sh
+++ b/t/t5411/test-0032-report-with-options.sh
@@ -13,8 +13,8 @@
 test_expect_success "proc-receive: report option without matching ok ($PROTOCOL)" '
 	test_must_fail git -C workbench push origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -247,10 +247,7 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0033-report-with-options--porcelain.sh b/t/t5411/test-0033-report-with-options--porcelain.sh
index 32ae26b..1148672 100644
--- a/t/t5411/test-0033-report-with-options--porcelain.sh
+++ b/t/t5411/test-0033-report-with-options--porcelain.sh
@@ -13,8 +13,8 @@
 test_expect_success "proc-receive: report option without matching ok ($PROTOCOL/porcelain)" '
 	test_must_fail git -C workbench push --porcelain origin \
 		HEAD:refs/for/main/topic \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -256,10 +256,7 @@
 	EOF
 	test_cmp expect actual &&
 
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0034-report-ft.sh b/t/t5411/test-0034-report-ft.sh
index c355c29..6e0d08b 100644
--- a/t/t5411/test-0034-report-ft.sh
+++ b/t/t5411/test-0034-report-ft.sh
@@ -28,13 +28,11 @@
 	 * [new reference] <COMMIT-B> -> refs/for/main/topic
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/for/main/topic
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             refs/for/main/topic(A)
diff --git a/t/t5411/test-0035-report-ft--porcelain.sh b/t/t5411/test-0035-report-ft--porcelain.sh
index 8ce4e58..81bae9f 100644
--- a/t/t5411/test-0035-report-ft--porcelain.sh
+++ b/t/t5411/test-0035-report-ft--porcelain.sh
@@ -29,13 +29,11 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/for/main/topic
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             refs/for/main/topic(A)
diff --git a/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
index fad8eea..be9b18b 100644
--- a/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
+++ b/t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
@@ -65,12 +65,10 @@
 	 <OID-A>..<OID-B> HEAD -> refs/changes/25/125/1
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #1 ($PROTOCOL)" '
@@ -142,12 +140,10 @@
 	 + <OID-B>...<OID-A> HEAD -> refs/changes/25/125/1 (forced update)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #2 ($PROTOCOL)" '
@@ -205,12 +201,10 @@
 	 <OID-A>..<OID-B> HEAD -> refs/changes/24/124/2
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #3 ($PROTOCOL)" '
diff --git a/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
index dc254d5..95fb89c 100644
--- a/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
+++ b/t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
@@ -51,12 +51,10 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL/porcelain)" '
@@ -114,12 +112,10 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL/porcelain)" '
@@ -163,10 +159,8 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5411/test-0038-report-mixed-refs.sh b/t/t5411/test-0038-report-mixed-refs.sh
index 0d071eb..5e00529 100644
--- a/t/t5411/test-0038-report-mixed-refs.sh
+++ b/t/t5411/test-0038-report-mixed-refs.sh
@@ -24,8 +24,8 @@
 		HEAD:refs/heads/foo \
 		HEAD:refs/for/main/topic \
 		HEAD:refs/for/next/topic3 \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -65,15 +65,13 @@
 	 ! [remote rejected] HEAD -> refs/for/next/topic3 (proc-receive failed to report status)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/bar
 	<COMMIT-A> refs/heads/baz
 	<COMMIT-A> refs/heads/foo
 	<COMMIT-B> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)             foo(A)  bar(A))  baz(A)
diff --git a/t/t5411/test-0039-report-mixed-refs--porcelain.sh b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
index d840991..8f891c5 100644
--- a/t/t5411/test-0039-report-mixed-refs--porcelain.sh
+++ b/t/t5411/test-0039-report-mixed-refs--porcelain.sh
@@ -24,8 +24,8 @@
 		HEAD:refs/heads/foo \
 		HEAD:refs/for/main/topic \
 		HEAD:refs/for/next/topic3 \
-		>out 2>&1 &&
-	make_user_friendly_and_stable_output <out >actual &&
+		>out-$test_count 2>&1 &&
+	make_user_friendly_and_stable_output <out-$test_count >actual &&
 	cat >expect <<-EOF &&
 	remote: # pre-receive hook
 	remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -66,15 +66,13 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/bar
 	<COMMIT-A> refs/heads/baz
 	<COMMIT-A> refs/heads/foo
 	<COMMIT-B> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(B)             foo(A)  bar(A))  baz(A)
diff --git a/t/t5411/test-0040-process-all-refs.sh b/t/t5411/test-0040-process-all-refs.sh
index 2565302..fdcdcc7 100644
--- a/t/t5411/test-0040-process-all-refs.sh
+++ b/t/t5411/test-0040-process-all-refs.sh
@@ -92,14 +92,12 @@
 	 + <OID-B>...<OID-A> HEAD -> refs/pull/124/head (forced update)
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/bar
 	<COMMIT-A> refs/heads/baz
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             bar(A)  baz(B)
diff --git a/t/t5411/test-0041-process-all-refs--porcelain.sh b/t/t5411/test-0041-process-all-refs--porcelain.sh
index e21420b..73b35fe 100644
--- a/t/t5411/test-0041-process-all-refs--porcelain.sh
+++ b/t/t5411/test-0041-process-all-refs--porcelain.sh
@@ -93,14 +93,12 @@
 	Done
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-B> refs/heads/bar
 	<COMMIT-A> refs/heads/baz
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             bar(A)  baz(B)
diff --git a/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
index 2e29518..7214647 100644
--- a/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
+++ b/t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
@@ -50,12 +50,10 @@
 	 * [new reference] v123 -> refs/pull/124/head
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	EOF
-	test_cmp expect actual
 '
 
 # Refs of upstream : main(A)
@@ -63,14 +61,12 @@
 test_expect_success "setup upstream: create tags/v123 ($PROTOCOL)" '
 	git -C "$upstream" update-ref refs/heads/topic $A &&
 	git -C "$upstream" update-ref refs/tags/v123 $TAG &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-A> refs/heads/topic
 	<TAG-v123> refs/tags/v123
 	EOF
-	test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook ($PROTOCOL)" '
@@ -125,11 +121,9 @@
 	 * [new reference] <COMMIT-A> -> refs/pull/124/head
 	EOF
 	test_cmp expect actual &&
-	git -C "$upstream" show-ref >out &&
-	make_user_friendly_and_stable_output <out >actual &&
-	cat >expect <<-EOF &&
+
+	test_cmp_refs -C "$upstream" <<-EOF
 	<COMMIT-A> refs/heads/main
 	<COMMIT-B> refs/heads/topic
 	EOF
-	test_cmp expect actual
 '
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 3557374..8a5d349 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -5,6 +5,9 @@
 
 test_description='Testing multi_ack pack fetching'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Test fetch-pack/upload-pack pair.
@@ -407,14 +410,14 @@
 	rm -rf myserver myclient &&
 	git init myserver &&
 
-	# Linked list of commits on master. The first is common; the rest are
+	# Linked list of commits on main. The first is common; the rest are
 	# not.
-	test_commit -C myserver first_master_commit &&
+	test_commit -C myserver first_main_commit &&
 	git clone "file://$(pwd)/myserver" myclient &&
 	test_commit_bulk -C myclient 255 &&
 
 	# Another linked list of commits on anotherbranch with no connection to
-	# master. The first is common; the rest are not.
+	# main. The first is common; the rest are not.
 	git -C myserver checkout --orphan anotherbranch &&
 	test_commit -C myserver first_anotherbranch_commit &&
 	git -C myclient fetch origin anotherbranch:refs/heads/anotherbranch &&
@@ -422,14 +425,14 @@
 	test_commit_bulk -C myclient 255 &&
 
 	# The new commit that the client wants to fetch.
-	git -C myserver checkout master &&
+	git -C myserver checkout main &&
 	test_commit -C myserver to_fetch &&
 
 	# The client will send (as "have"s) all 256 commits in anotherbranch
 	# first. The 256th commit is common between the client and the server,
 	# and should reset in_vain. This allows negotiation to continue until
 	# the client reports that first_anotherbranch_commit is common.
-	git -C myclient fetch --progress origin master 2>log &&
+	git -C myclient fetch --progress origin main 2>log &&
 	test_i18ngrep "Total 3 " log
 '
 
@@ -544,7 +547,7 @@
 		cd client &&
 		test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy 2>../error-m
 	) &&
-	test_i18ncmp expect-error error-m
+	test_cmp expect-error error-m
 '
 
 test_expect_success 'test missing ref after existing' '
@@ -552,7 +555,7 @@
 		cd client &&
 		test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy 2>../error-em
 	) &&
-	test_i18ncmp expect-error error-em
+	test_cmp expect-error error-em
 '
 
 test_expect_success 'test missing ref before existing' '
@@ -560,7 +563,7 @@
 		cd client &&
 		test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A 2>../error-me
 	) &&
-	test_i18ncmp expect-error error-me
+	test_cmp expect-error error-me
 '
 
 test_expect_success 'test --all, --depth, and explicit head' '
@@ -637,7 +640,7 @@
 		mkdir repo2 &&
 		cd repo2 &&
 		git init &&
-		git fetch --depth=2 ../.git master:branch &&
+		git fetch --depth=2 ../.git main:branch &&
 		git fsck
 	)
 '
@@ -662,7 +665,7 @@
 
 	git init client &&
 	git -C client fetch-pack ../server \
-		$(git -C server rev-parse refs/heads/master)
+		$(git -C server rev-parse refs/heads/main)
 '
 
 test_expect_success 'fetch-pack can fetch a raw sha1 overlapping a named ref' '
@@ -688,7 +691,7 @@
 	# Some protocol versions (e.g. 2) support fetching
 	# unadvertised objects, so restrict this test to v0.
 	test_must_fail env GIT_TEST_PROTOCOL_VERSION=0 git -C client fetch-pack ../server \
-		$(git -C server rev-parse refs/heads/master^) 2>err &&
+		$(git -C server rev-parse refs/heads/main^) 2>err &&
 	test_i18ngrep "Server does not allow request for unadvertised object" err
 '
 
@@ -822,7 +825,7 @@
 
 test_expect_success 'fetch shallow since ...' '
 	git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
-	git -C shallow11 log --pretty=tformat:%s origin/master >actual &&
+	git -C shallow11 log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	three
 	two
@@ -863,7 +866,7 @@
 	(
 	cd shallow-since-graph &&
 	test_commit base &&
-	test_commit master &&
+	test_commit main &&
 	git checkout -b other HEAD^ &&
 	test_commit other &&
 	git commit-graph write --reachable &&
@@ -874,7 +877,7 @@
 	$(echo "object-format=$(test_oid algo)" | packetize)
 	00010013deepen-since 1
 	$(echo "want $(git rev-parse other)" | packetize)
-	$(echo "have $(git rev-parse master)" | packetize)
+	$(echo "have $(git rev-parse main)" | packetize)
 	0000
 	EOF
 	)
@@ -896,7 +899,7 @@
 
 test_expect_success 'fetch exclude tag one' '
 	git -C shallow12 fetch --shallow-exclude one origin &&
-	git -C shallow12 log --pretty=tformat:%s origin/master >actual &&
+	git -C shallow12 log --pretty=tformat:%s origin/main >actual &&
 	test_write_lines three two >expected &&
 	test_cmp expected actual
 '
@@ -910,11 +913,11 @@
 	test_commit three &&
 	git clone --depth 1 "file://$(pwd)/." deepen &&
 	test_commit four &&
-	git -C deepen log --pretty=tformat:%s master >actual &&
+	git -C deepen log --pretty=tformat:%s main >actual &&
 	echo three >expected &&
 	test_cmp expected actual &&
 	git -C deepen fetch --deepen=1 &&
-	git -C deepen log --pretty=tformat:%s origin/master >actual &&
+	git -C deepen log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	four
 	three
diff --git a/t/t5501-fetch-push-alternates.sh b/t/t5501-fetch-push-alternates.sh
index 1bc57ac..66f19a4 100755
--- a/t/t5501-fetch-push-alternates.sh
+++ b/t/t5501-fetch-push-alternates.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='fetch/push involving alternates'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 count_objects () {
@@ -45,7 +48,7 @@
 test_expect_success 'pushing into a repository with the same alternate' '
 	(
 		cd one &&
-		git push ../receiver master:refs/heads/it
+		git push ../receiver main:refs/heads/it
 	) &&
 	(
 		cd receiver &&
@@ -57,7 +60,7 @@
 test_expect_success 'fetching from a repository with the same alternate' '
 	(
 		cd fetcher &&
-		git fetch ../one master:refs/heads/it &&
+		git fetch ../one main:refs/heads/it &&
 		count_objects >../fetcher.count
 	) &&
 	test_cmp one.count fetcher.count
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
index 7a46cbd..8c05c77 100755
--- a/t/t5502-quickfetch.sh
+++ b/t/t5502-quickfetch.sh
@@ -2,6 +2,9 @@
 
 test_description='test quickfetch from local'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -108,14 +111,14 @@
 				q
 			}"
 	) ) &&
-	origin_master=$( (
+	origin_main=$( (
 		cd quickclone &&
-		git rev-parse origin/master
+		git rev-parse origin/main
 	) ) &&
 	echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
 	test $obj_cnt -eq 0 &&
 	test $pck_cnt -eq 0 &&
-	test z$origin_master = z$(git rev-parse master)
+	test z$origin_main = z$(git rev-parse main)
 
 '
 
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 6041a4d..195fc64 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -2,6 +2,9 @@
 
 test_description='test automatic tag following'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # End state of the repository:
@@ -11,7 +14,7 @@
 #   L - A ------ O ------ B
 #    \   \                 \
 #     \   C - origin/cat    \
-#      origin/master         master
+#      origin/main           main
 
 test_expect_success setup '
 	test_tick &&
@@ -57,7 +60,7 @@
 	(
 		cd cloned &&
 		GIT_TRACE_PACKET=$UPATH git fetch &&
-		test $A = $(git rev-parse --verify origin/master)
+		test $A = $(git rev-parse --verify origin/main)
 	) &&
 	get_needs $U >actual &&
 	test_cmp expect actual
@@ -72,7 +75,7 @@
 	git add file &&
 	git commit -m C &&
 	C=$(git rev-parse --verify HEAD) &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'setup expect' '
@@ -123,7 +126,7 @@
 	(
 		cd cloned &&
 		GIT_TRACE_PACKET=$UPATH git fetch &&
-		test $B = $(git rev-parse --verify origin/master) &&
+		test $B = $(git rev-parse --verify origin/main) &&
 		test $B = $(git rev-parse --verify tag2^0) &&
 		test $S = $(git rev-parse --verify tag2)
 	) &&
@@ -138,7 +141,7 @@
 EOF
 '
 
-test_expect_success 'new clone fetch master and tags' '
+test_expect_success 'new clone fetch main and tags' '
 	test_might_fail git branch -D cat &&
 	rm -f $U &&
 	(
@@ -147,7 +150,7 @@
 		git init &&
 		git remote add origin .. &&
 		GIT_TRACE_PACKET=$UPATH git fetch &&
-		test $B = $(git rev-parse --verify origin/master) &&
+		test $B = $(git rev-parse --verify origin/main) &&
 		test $S = $(git rev-parse --verify tag2) &&
 		test $B = $(git rev-parse --verify tag2^0) &&
 		test $T = $(git rev-parse --verify tag1) &&
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 1a16ac4..6e5a9c2 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='fetch/receive strict mode'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup and inject "corrupt or missing" object' '
@@ -25,7 +28,7 @@
 		cd dst &&
 		git config fetch.fsckobjects false &&
 		git config transfer.fsckobjects false &&
-		test_must_fail git fetch ../.git master
+		test_must_fail git fetch ../.git main
 	)
 '
 
@@ -36,7 +39,7 @@
 		cd dst &&
 		git config fetch.fsckobjects false &&
 		git config transfer.fsckobjects true &&
-		test_must_fail git fetch ../.git master
+		test_must_fail git fetch ../.git main
 	)
 '
 
@@ -47,7 +50,7 @@
 		cd dst &&
 		git config fetch.fsckobjects true &&
 		git config transfer.fsckobjects false &&
-		test_must_fail git fetch ../.git master
+		test_must_fail git fetch ../.git main
 	)
 '
 
@@ -57,13 +60,13 @@
 	(
 		cd dst &&
 		git config transfer.fsckobjects true &&
-		test_must_fail git fetch ../.git master
+		test_must_fail git fetch ../.git main
 	)
 '
 
 cat >exp <<EOF
 To dst
-!	refs/heads/master:refs/heads/test	[remote rejected] (missing necessary objects)
+!	refs/heads/main:refs/heads/test	[remote rejected] (missing necessary objects)
 Done
 EOF
 
@@ -75,7 +78,7 @@
 		git config fetch.fsckobjects false &&
 		git config transfer.fsckobjects false
 	) &&
-	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_must_fail git push --porcelain dst main:refs/heads/test >act &&
 	test_cmp exp act
 '
 
@@ -87,13 +90,13 @@
 		git config receive.fsckobjects false &&
 		git config transfer.fsckobjects true
 	) &&
-	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_must_fail git push --porcelain dst main:refs/heads/test >act &&
 	test_cmp exp act
 '
 
 cat >exp <<EOF
 To dst
-!	refs/heads/master:refs/heads/test	[remote rejected] (unpacker error)
+!	refs/heads/main:refs/heads/test	[remote rejected] (unpacker error)
 EOF
 
 test_expect_success 'push with receive.fsckobjects' '
@@ -104,7 +107,7 @@
 		git config receive.fsckobjects true &&
 		git config transfer.fsckobjects false
 	) &&
-	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_must_fail git push --porcelain dst main:refs/heads/test >act &&
 	test_cmp exp act
 '
 
@@ -115,7 +118,7 @@
 		cd dst &&
 		git config transfer.fsckobjects true
 	) &&
-	test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+	test_must_fail git push --porcelain dst main:refs/heads/test >act &&
 	test_cmp exp act
 '
 
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index ecadf02..8c462f2 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -2,6 +2,9 @@
 
 test_description='git remote porcelain-ish'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 setup_repository () {
@@ -56,7 +59,7 @@
 	git remote add myremote git@host.com:team/repo.git
 '
 
-test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
+test_expect_success 'remote information for the origin' '
 	(
 		cd test &&
 		tokens_match origin "$(git remote)" &&
@@ -78,7 +81,7 @@
 	)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+test_expect_success 'check remote-tracking' '
 	(
 		cd test &&
 		check_remote_track origin main side &&
@@ -104,7 +107,7 @@
 	)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'remove remote' '
+test_expect_success 'remove remote' '
 	(
 		cd test &&
 		tokens_match origin "$(git remote)" &&
@@ -137,8 +140,8 @@
 		git remote rm oops 2>actual2 &&
 		git branch -d foobranch &&
 		git tag -d footag &&
-		test_i18ncmp expect1 actual1 &&
-		test_i18ncmp expect2 actual2
+		test_cmp expect1 actual1 &&
+		test_cmp expect2 actual2
 	)
 '
 
@@ -147,7 +150,7 @@
 		cd test &&
 		echo "error: No such remote: '\''foo'\''" >expect &&
 		test_expect_code 2 git remote rm foo 2>actual &&
-		test_i18ncmp expect actual
+		test_cmp expect actual
 	)
 '
 
@@ -175,7 +178,7 @@
 		cd test &&
 		echo "error: No such remote: '\''foo'\''" >expect &&
 		test_expect_code 2 git remote rename foo bar 2>actual &&
-		test_i18ncmp expect actual
+		test_cmp expect actual
 	)
 '
 
@@ -183,14 +186,14 @@
 	test_config remote.foo.vcs bar &&
 	echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
 	test_must_fail git remote rename foo invalid...name 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'add existing foreign_vcs remote' '
 	test_config remote.foo.vcs bar &&
 	echo "error: remote foo already exists." >expect &&
 	test_expect_code 3 git remote add foo bar 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'add existing foreign_vcs remote' '
@@ -198,13 +201,13 @@
 	test_config remote.bar.vcs bar &&
 	echo "error: remote bar already exists." >expect &&
 	test_expect_code 3 git remote rename foo bar 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'add invalid foreign_vcs remote' '
 	echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
 	test_must_fail git remote add invalid...name bar 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >test/expect <<EOF
@@ -264,7 +267,7 @@
 		git config --add remote.two.push refs/heads/main:refs/heads/another &&
 		git remote show origin two >output &&
 		git branch -d rebase octopus &&
-		test_i18ncmp expect output
+		test_cmp expect output
 	)
 '
 
@@ -291,7 +294,7 @@
 		cd test &&
 		git remote show -n origin >output &&
 		mv ../one.unreachable ../one &&
-		test_i18ncmp expect output
+		test_cmp expect output
 	)
 '
 
@@ -334,7 +337,7 @@
 		git fetch two "refs/heads/*:refs/remotes/two/*" &&
 		git remote set-head --auto two >output 2>&1 &&
 		echo "two/HEAD set to main" >expect &&
-		test_i18ncmp expect output
+		test_cmp expect output
 	)
 '
 
@@ -366,7 +369,7 @@
 		git remote prune --dry-run origin >output &&
 		git rev-parse refs/remotes/origin/side2 &&
 		test_must_fail git rev-parse refs/remotes/origin/side &&
-		test_i18ncmp expect output
+		test_cmp expect output
 	)
 '
 
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 83d5558..8f150c0 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git remote group handling'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 mark() {
@@ -31,8 +34,8 @@
 test_expect_success 'setup' '
 	mkdir one && (cd one && git init) &&
 	mkdir two && (cd two && git init) &&
-	git remote add -m master one one &&
-	git remote add -m master two two
+	git remote add -m main one one &&
+	git remote add -m main two two
 '
 
 test_expect_success 'no group updates all' '
diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh
index a67f792..31553b4 100755
--- a/t/t5509-fetch-push-namespaces.sh
+++ b/t/t5509-fetch-push-namespaces.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='fetch/push involving ref namespaces'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -27,9 +30,9 @@
 test_expect_success 'pushing into a repository using a ref namespace' '
 	(
 		cd original &&
-		git push pushee-namespaced master &&
+		git push pushee-namespaced main &&
 		git ls-remote pushee-namespaced >actual &&
-		printf "$commit1\trefs/heads/master\n" >expected &&
+		printf "$commit1\trefs/heads/main\n" >expected &&
 		test_cmp expected actual &&
 		git push pushee-namespaced --tags &&
 		git ls-remote pushee-namespaced >actual &&
@@ -56,7 +59,7 @@
 		cd puller &&
 		git remote add -f pushee-namespaced "ext::git --namespace=namespace %s ../pushee" &&
 		git for-each-ref refs/ >actual &&
-		printf "$commit1 commit\trefs/remotes/pushee-namespaced/master\n" >expected &&
+		printf "$commit1 commit\trefs/remotes/pushee-namespaced/main\n" >expected &&
 		printf "$commit0 commit\trefs/tags/0\n" >>expected &&
 		printf "$commit1 commit\trefs/tags/1\n" >>expected &&
 		test_cmp expected actual
@@ -76,7 +79,7 @@
 	(
 		cd mirror &&
 		git for-each-ref refs/ >actual &&
-		printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/master\n" >expected &&
+		printf "$commit1 commit\trefs/namespaces/namespace/refs/heads/main\n" >expected &&
 		printf "$commit0 commit\trefs/namespaces/namespace/refs/tags/0\n" >>expected &&
 		printf "$commit1 commit\trefs/namespaces/namespace/refs/tags/1\n" >>expected &&
 		test_cmp expected actual
@@ -87,7 +90,7 @@
 	GIT_NAMESPACE=namespace \
 		git -C pushee -c transfer.hideRefs=refs/tags \
 		ls-remote "ext::git %s ." >actual &&
-	printf "$commit1\trefs/heads/master\n" >expected &&
+	printf "$commit1\trefs/heads/main\n" >expected &&
 	test_cmp expected actual
 '
 
@@ -95,7 +98,7 @@
 	GIT_NAMESPACE=namespace \
 		git -C pushee -c transfer.hideRefs=refs/namespaces/namespace/refs/tags \
 		ls-remote "ext::git %s ." >actual &&
-	printf "$commit1\trefs/heads/master\n" >expected &&
+	printf "$commit1\trefs/heads/main\n" >expected &&
 	printf "$commit0\trefs/tags/0\n" >>expected &&
 	printf "$commit1\trefs/tags/1\n" >>expected &&
 	test_cmp expected actual
@@ -105,23 +108,23 @@
 	GIT_NAMESPACE=namespace \
 		git -C pushee -c transfer.hideRefs="^refs/namespaces/namespace/refs/tags" \
 		ls-remote "ext::git %s ." >actual &&
-	printf "$commit1\trefs/heads/master\n" >expected &&
+	printf "$commit1\trefs/heads/main\n" >expected &&
 	test_cmp expected actual
 '
 
 test_expect_success 'try to update a hidden ref' '
-	test_config -C pushee transfer.hideRefs refs/heads/master &&
-	test_must_fail git -C original push pushee-namespaced master
+	test_config -C pushee transfer.hideRefs refs/heads/main &&
+	test_must_fail git -C original push pushee-namespaced main
 '
 
 test_expect_success 'try to update a ref that is not hidden' '
-	test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/master &&
-	git -C original push pushee-namespaced master
+	test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/main &&
+	git -C original push pushee-namespaced main
 '
 
 test_expect_success 'try to update a hidden full ref' '
-	test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/master" &&
-	test_must_fail git -C original push pushee-namespaced master
+	test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/main" &&
+	test_must_fail git -C original push pushee-namespaced main
 '
 
 test_expect_success 'set up ambiguous HEAD' '
@@ -157,9 +160,9 @@
 		cd original &&
 		git init unborn &&
 		git remote add unborn-namespaced "ext::git --namespace=namespace %s unborn" &&
-		test_must_fail git push unborn-namespaced HEAD:master &&
+		test_must_fail git push unborn-namespaced HEAD:main &&
 		git -C unborn config receive.denyCurrentBranch updateInstead &&
-		git push unborn-namespaced HEAD:master
+		git push unborn-namespaced HEAD:main
 	)
 '
 
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 2013051..e83b2a6 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -5,23 +5,14 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
 
 D=$(pwd)
 
-test_bundle_object_count () {
-	git verify-pack -v "$1" >verify.out &&
-	test "$2" = $(grep "^$OID_REGEX " verify.out | wc -l)
-}
-
-convert_bundle_to_pack () {
-	while read x && test -n "$x"
-	do
-		:;
-	done
-	cat
-}
-
 test_expect_success setup '
 	echo >file original &&
 	git add file &&
@@ -176,6 +167,174 @@
 	git rev-parse sometag
 '
 
+test_expect_success 'fetch --atomic works with a single branch' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	git branch atomic-branch &&
+	oid=$(git rev-parse atomic-branch) &&
+	echo "$oid" >expected &&
+
+	git -C atomic fetch --atomic origin &&
+	git -C atomic rev-parse origin/atomic-branch >actual &&
+	test_cmp expected actual &&
+	test $oid = "$(git -C atomic rev-parse --verify FETCH_HEAD)"
+'
+
+test_expect_success 'fetch --atomic works with multiple branches' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	git branch atomic-branch-1 &&
+	git branch atomic-branch-2 &&
+	git branch atomic-branch-3 &&
+	git rev-parse refs/heads/atomic-branch-1 refs/heads/atomic-branch-2 refs/heads/atomic-branch-3 >actual &&
+
+	git -C atomic fetch --atomic origin &&
+	git -C atomic rev-parse refs/remotes/origin/atomic-branch-1 refs/remotes/origin/atomic-branch-2 refs/remotes/origin/atomic-branch-3 >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'fetch --atomic works with mixed branches and tags' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	git branch atomic-mixed-branch &&
+	git tag atomic-mixed-tag &&
+	git rev-parse refs/heads/atomic-mixed-branch refs/tags/atomic-mixed-tag >actual &&
+
+	git -C atomic fetch --tags --atomic origin &&
+	git -C atomic rev-parse refs/remotes/origin/atomic-mixed-branch refs/tags/atomic-mixed-tag >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'fetch --atomic prunes references' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git branch atomic-prune-delete &&
+	git clone . atomic &&
+	git branch --delete atomic-prune-delete &&
+	git branch atomic-prune-create &&
+	git rev-parse refs/heads/atomic-prune-create >actual &&
+
+	git -C atomic fetch --prune --atomic origin &&
+	test_must_fail git -C atomic rev-parse refs/remotes/origin/atomic-prune-delete &&
+	git -C atomic rev-parse refs/remotes/origin/atomic-prune-create >expected &&
+	test_cmp expected actual
+'
+
+test_expect_success 'fetch --atomic aborts with non-fast-forward update' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git branch atomic-non-ff &&
+	git clone . atomic &&
+	git rev-parse HEAD >actual &&
+
+	git branch atomic-new-branch &&
+	parent_commit=$(git rev-parse atomic-non-ff~) &&
+	git update-ref refs/heads/atomic-non-ff $parent_commit &&
+
+	test_must_fail git -C atomic fetch --atomic origin refs/heads/*:refs/remotes/origin/* &&
+	test_must_fail git -C atomic rev-parse refs/remotes/origin/atomic-new-branch &&
+	git -C atomic rev-parse refs/remotes/origin/atomic-non-ff >expected &&
+	test_cmp expected actual &&
+	test_must_be_empty atomic/.git/FETCH_HEAD
+'
+
+test_expect_success 'fetch --atomic executes a single reference transaction only' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	git branch atomic-hooks-1 &&
+	git branch atomic-hooks-2 &&
+	head_oid=$(git rev-parse HEAD) &&
+
+	cat >expected <<-EOF &&
+		prepared
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-1
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-2
+		committed
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-1
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-2
+	EOF
+
+	rm -f atomic/actual &&
+	write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+		( echo "$*" && cat ) >>actual
+	EOF
+
+	git -C atomic fetch --atomic origin &&
+	test_cmp expected atomic/actual
+'
+
+test_expect_success 'fetch --atomic aborts all reference updates if hook aborts' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	git branch atomic-hooks-abort-1 &&
+	git branch atomic-hooks-abort-2 &&
+	git branch atomic-hooks-abort-3 &&
+	git tag atomic-hooks-abort &&
+	head_oid=$(git rev-parse HEAD) &&
+
+	cat >expected <<-EOF &&
+		prepared
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-1
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-2
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-3
+		$ZERO_OID $head_oid refs/tags/atomic-hooks-abort
+		aborted
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-1
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-2
+		$ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-3
+		$ZERO_OID $head_oid refs/tags/atomic-hooks-abort
+	EOF
+
+	rm -f atomic/actual &&
+	write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+		( echo "$*" && cat ) >>actual
+		exit 1
+	EOF
+
+	git -C atomic for-each-ref >expected-refs &&
+	test_must_fail git -C atomic fetch --tags --atomic origin &&
+	git -C atomic for-each-ref >actual-refs &&
+	test_cmp expected-refs actual-refs &&
+	test_must_be_empty atomic/.git/FETCH_HEAD
+'
+
+test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
+	test_when_finished "rm -rf \"$D\"/atomic" &&
+
+	cd "$D" &&
+	git clone . atomic &&
+	oid=$(git rev-parse HEAD) &&
+
+	git branch atomic-fetch-head-1 &&
+	git -C atomic fetch --atomic origin atomic-fetch-head-1 &&
+	test_line_count = 1 atomic/.git/FETCH_HEAD &&
+
+	git branch atomic-fetch-head-2 &&
+	git -C atomic fetch --atomic --append origin atomic-fetch-head-2 &&
+	test_line_count = 2 atomic/.git/FETCH_HEAD &&
+	cp atomic/.git/FETCH_HEAD expected &&
+
+	write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+		exit 1
+	EOF
+
+	git branch atomic-fetch-head-3 &&
+	test_must_fail git -C atomic fetch --atomic --append origin atomic-fetch-head-3 &&
+	test_cmp expected atomic/.git/FETCH_HEAD
+'
+
 test_expect_success '--refmap="" ignores configured refspec' '
 	cd "$TRASH_DIRECTORY" &&
 	git clone "$D" remote-refs &&
@@ -312,9 +471,7 @@
 
 test_expect_success 'bundle 1 has only 3 files ' '
 	cd "$D" &&
-	convert_bundle_to_pack <bundle1 >bundle.pack &&
-	git index-pack bundle.pack &&
-	test_bundle_object_count bundle.pack 3
+	test_bundle_object_count bundle1 3
 '
 
 test_expect_success 'unbundle 2' '
@@ -329,9 +486,7 @@
 	git add file2 &&
 	git commit -m add.file2 file2 &&
 	git bundle create bundle3 -1 HEAD &&
-	convert_bundle_to_pack <bundle3 >bundle.pack &&
-	git index-pack bundle.pack &&
-	test_bundle_object_count bundle.pack 3
+	test_bundle_object_count bundle3 3
 '
 
 test_expect_success 'bundle should be able to create a full history' '
@@ -884,9 +1039,7 @@
 	git merge otherside &&
 	ad=$(git log --no-walk --format=%ad HEAD) &&
 	git bundle create twoside-boundary.bdl main --since="$ad" &&
-	convert_bundle_to_pack <twoside-boundary.bdl >twoside-boundary.pack &&
-	pack=$(git index-pack --fix-thin --stdin <twoside-boundary.pack) &&
-	test_bundle_object_count .git/objects/pack/pack-${pack##pack	}.pack 3
+	test_bundle_object_count --thin twoside-boundary.bdl 3
 '
 
 test_expect_success 'fetch --prune prints the remotes url' '
@@ -898,7 +1051,7 @@
 		git fetch --prune origin 2>&1 | head -n1 >../actual
 	) &&
 	echo "From ${D}/." >expect &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'branchname D/F conflict resolved by --prune' '
@@ -944,7 +1097,7 @@
 	)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
+test_expect_success 'fetch aligned output' '
 	git clone . full-output &&
 	test_commit looooooooooooong-tag &&
 	(
@@ -959,7 +1112,7 @@
 	test_cmp expect actual
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
+test_expect_success 'fetch compact output' '
 	git clone . compact &&
 	test_commit extraaa &&
 	(
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
index f808649..be025b9 100755
--- a/t/t5511-refspec.sh
+++ b/t/t5511-refspec.sh
@@ -33,7 +33,7 @@
 test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
 test_refspec push 'refs/heads/*:refs/remotes/frotz'		invalid
 test_refspec push 'refs/heads:refs/remotes/frotz/*'		invalid
-test_refspec push 'refs/heads/master:refs/remotes/frotz/xyzzy'
+test_refspec push 'refs/heads/main:refs/remotes/frotz/xyzzy'
 
 
 # These have invalid LHS, but we do not have a formal "valid sha-1
@@ -41,18 +41,18 @@
 # code.  They will be caught downstream anyway, but we may want to
 # have tighter check later...
 
-: test_refspec push 'refs/heads/master::refs/remotes/frotz/xyzzy'	invalid
+: test_refspec push 'refs/heads/main::refs/remotes/frotz/xyzzy'	invalid
 : test_refspec push 'refs/heads/maste :refs/remotes/frotz/xyzzy'	invalid
 
 test_refspec fetch 'refs/heads/*:refs/remotes/frotz/*'
 test_refspec fetch 'refs/heads/*:refs/remotes/frotz'		invalid
 test_refspec fetch 'refs/heads:refs/remotes/frotz/*'		invalid
-test_refspec fetch 'refs/heads/master:refs/remotes/frotz/xyzzy'
-test_refspec fetch 'refs/heads/master::refs/remotes/frotz/xyzzy'	invalid
+test_refspec fetch 'refs/heads/main:refs/remotes/frotz/xyzzy'
+test_refspec fetch 'refs/heads/main::refs/remotes/frotz/xyzzy'	invalid
 test_refspec fetch 'refs/heads/maste :refs/remotes/frotz/xyzzy'	invalid
 
-test_refspec push 'master~1:refs/remotes/frotz/backup'
-test_refspec fetch 'master~1:refs/remotes/frotz/backup'		invalid
+test_refspec push 'main~1:refs/remotes/frotz/backup'
+test_refspec fetch 'main~1:refs/remotes/frotz/backup'		invalid
 test_refspec push 'HEAD~4:refs/remotes/frotz/new'
 test_refspec fetch 'HEAD~4:refs/remotes/frotz/new'		invalid
 
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
index e98c3a0..f53f588 100755
--- a/t/t5512-ls-remote.sh
+++ b/t/t5512-ls-remote.sh
@@ -2,6 +2,9 @@
 
 test_description='git ls-remote'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 generate_references () {
@@ -118,7 +121,7 @@
 	echo "From $URL" >exp_err &&
 
 	git remote add other $URL &&
-	git config branch.master.remote other &&
+	git config branch.main.remote other &&
 
 	git ls-remote 2>actual_err >actual &&
 	test_cmp exp_err actual_err &&
@@ -129,9 +132,9 @@
 	if test_have_prereq MINGW
 	then
 		# Windows does not like asterisks in pathname
-		does_not_exist=master
+		does_not_exist=main
 	else
-		does_not_exist="refs*master"
+		does_not_exist="refs*main"
 	fi &&
 	cat >exp <<-EOF &&
 	fatal: '\''$does_not_exist'\'' does not appear to be a git repository
@@ -144,10 +147,10 @@
 	# Do not expect "git ls-remote <pattern>" to work; ls-remote needs
 	# <remote> if you want to feed <pattern>, just like you cannot say
 	# fetch <branch>.
-	# We could just as easily have used "master"; the "*" emphasizes its
+	# We could just as easily have used "main"; the "*" emphasizes its
 	# role as a pattern.
 	test_must_fail git ls-remote "$does_not_exist" >actual 2>&1 &&
-	test_i18ncmp exp actual
+	test_cmp exp actual
 '
 
 test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
@@ -217,14 +220,14 @@
 
 test_expect_success 'ls-remote --symref' '
 	git fetch origin &&
-	echo "ref: refs/heads/master	HEAD" >expect &&
+	echo "ref: refs/heads/main	HEAD" >expect &&
 	generate_references \
 		HEAD \
-		refs/heads/master >>expect &&
+		refs/heads/main >>expect &&
 	oid=$(git rev-parse HEAD) &&
 	echo "$oid	refs/remotes/origin/HEAD" >>expect &&
 	generate_references \
-		refs/remotes/origin/master \
+		refs/remotes/origin/main \
 		refs/tags/mark \
 		refs/tags/mark1.1 \
 		refs/tags/mark1.10 \
@@ -238,7 +241,7 @@
 test_expect_success 'ls-remote with filtered symref (refname)' '
 	rev=$(git rev-parse HEAD) &&
 	cat >expect <<-EOF &&
-	ref: refs/heads/master	HEAD
+	ref: refs/heads/main	HEAD
 	$rev	HEAD
 	EOF
 	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
@@ -252,7 +255,7 @@
 	cat >expect <<-EOF &&
 	ref: refs/tags/mark	refs/heads/foo
 	$rev	refs/heads/foo
-	$rev	refs/heads/master
+	$rev	refs/heads/main
 	EOF
 	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
 	# protocol v0 here.
@@ -263,7 +266,7 @@
 test_expect_success 'ls-remote --symref omits filtered-out matches' '
 	cat >expect <<-EOF &&
 	$rev	refs/heads/foo
-	$rev	refs/heads/master
+	$rev	refs/heads/main
 	EOF
 	# Protocol v2 supports sending symrefs for refs other than HEAD, so use
 	# protocol v0 here.
@@ -330,10 +333,10 @@
 
 test_expect_success 'ls-remote patterns work with all protocol versions' '
 	git for-each-ref --format="%(objectname)	%(refname)" \
-		refs/heads/master refs/remotes/origin/master >expect &&
-	git -c protocol.version=1 ls-remote . master >actual.v1 &&
+		refs/heads/main refs/remotes/origin/main >expect &&
+	git -c protocol.version=1 ls-remote . main >actual.v1 &&
 	test_cmp expect actual.v1 &&
-	git -c protocol.version=2 ls-remote . master >actual.v2 &&
+	git -c protocol.version=2 ls-remote . main >actual.v2 &&
 	test_cmp expect actual.v2
 '
 
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index bd202ec..511ba3b 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -2,6 +2,9 @@
 
 test_description='fetch --all works correctly'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 setup_repository () {
@@ -17,7 +20,7 @@
 	git add elif &&
 	test_tick &&
 	git commit -m "Second" &&
-	git checkout master
+	git checkout main
 	)
 }
 
@@ -32,16 +35,16 @@
 '
 
 cat > test/expect << EOF
-  one/master
+  one/main
   one/side
-  origin/HEAD -> origin/master
-  origin/master
+  origin/HEAD -> origin/main
+  origin/main
   origin/side
   three/another
-  three/master
+  three/main
   three/side
   two/another
-  two/master
+  two/main
   two/side
 EOF
 
@@ -70,15 +73,15 @@
 test_expect_success 'git fetch --all does not allow non-option arguments' '
 	(cd test &&
 	 test_must_fail git fetch --all origin &&
-	 test_must_fail git fetch --all origin master)
+	 test_must_fail git fetch --all origin main)
 '
 
 cat > expect << EOF
-  origin/HEAD -> origin/master
-  origin/master
+  origin/HEAD -> origin/main
+  origin/main
   origin/side
   three/another
-  three/master
+  three/main
   three/side
 EOF
 
@@ -92,10 +95,10 @@
 '
 
 cat > expect << EOF
-  one/master
+  one/main
   one/side
   two/another
-  two/master
+  two/main
   two/side
 EOF
 
@@ -133,13 +136,13 @@
 '
 
 cat > expect << EOF
-  one/master
+  one/main
   one/side
   three/another
-  three/master
+  three/main
   three/side
   two/another
-  two/master
+  two/main
   two/side
 EOF
 
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 3ed121d..f11742e 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -14,6 +14,9 @@
 * reflogs
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 D=$(pwd)
@@ -120,13 +123,13 @@
 	git add path1 &&
 	test_tick &&
 	git commit -a -m repo &&
-	the_first_commit=$(git show-ref -s --verify refs/heads/master) &&
+	the_first_commit=$(git show-ref -s --verify refs/heads/main) &&
 
 	>path2 &&
 	git add path2 &&
 	test_tick &&
 	git commit -a -m second &&
-	the_commit=$(git show-ref -s --verify refs/heads/master)
+	the_commit=$(git show-ref -s --verify refs/heads/main)
 
 '
 
@@ -134,9 +137,9 @@
 	mk_empty testrepo &&
 	(
 		cd testrepo &&
-		git fetch .. refs/heads/master:refs/remotes/origin/master &&
+		git fetch .. refs/heads/main:refs/remotes/origin/main &&
 
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -150,7 +153,7 @@
 		git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
 		git fetch up &&
 
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -166,7 +169,7 @@
 		git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
 		git fetch up &&
 
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -182,7 +185,7 @@
 		git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
 		git fetch up &&
 
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -191,10 +194,10 @@
 test_expect_success 'push without wildcard' '
 	mk_empty testrepo &&
 
-	git push testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push testrepo refs/heads/main:refs/remotes/origin/main &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -206,7 +209,7 @@
 	git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -216,10 +219,10 @@
 	mk_empty testrepo &&
 	TRASH="$(pwd)/" &&
 	test_config "url.$TRASH.insteadOf" trash/ &&
-	git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push trash/testrepo refs/heads/main:refs/remotes/origin/main &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -229,10 +232,10 @@
 	mk_empty testrepo &&
 	TRASH="$(pwd)/" &&
 	test_config "url.$TRASH.pushInsteadOf" trash/ &&
-	git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push trash/testrepo refs/heads/main:refs/remotes/origin/main &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -244,10 +247,10 @@
 	test_config "url.trash3/.pushInsteadOf" trash/wrong &&
 	test_config remote.r.url trash/wrong &&
 	test_config remote.r.pushurl "testrepo/" &&
-	git push r refs/heads/master:refs/remotes/origin/master &&
+	git push r refs/heads/main:refs/remotes/origin/main &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	)
@@ -255,101 +258,101 @@
 
 test_expect_success 'push with matching heads' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	git push testrepo : &&
-	check_push_result testrepo $the_commit heads/master
+	check_push_result testrepo $the_commit heads/main
 
 '
 
 test_expect_success 'push with matching heads on the command line' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	git push testrepo : &&
-	check_push_result testrepo $the_commit heads/master
+	check_push_result testrepo $the_commit heads/main
 
 '
 
 test_expect_success 'failed (non-fast-forward) push with matching heads' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	git push testrepo : &&
 	git commit --amend -massaged &&
 	test_must_fail git push testrepo &&
-	check_push_result testrepo $the_commit heads/master &&
+	check_push_result testrepo $the_commit heads/main &&
 	git reset --hard $the_commit
 
 '
 
 test_expect_success 'push --force with matching heads' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	git push testrepo : &&
 	git commit --amend -massaged &&
 	git push --force testrepo : &&
-	! check_push_result testrepo $the_commit heads/master &&
+	! check_push_result testrepo $the_commit heads/main &&
 	git reset --hard $the_commit
 
 '
 
 test_expect_success 'push with matching heads and forced update' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	git push testrepo : &&
 	git commit --amend -massaged &&
 	git push testrepo +: &&
-	! check_push_result testrepo $the_commit heads/master &&
+	! check_push_result testrepo $the_commit heads/main &&
 	git reset --hard $the_commit
 
 '
 
 test_expect_success 'push with no ambiguity (1)' '
 
-	mk_test testrepo heads/master &&
-	git push testrepo master:master &&
-	check_push_result testrepo $the_commit heads/master
+	mk_test testrepo heads/main &&
+	git push testrepo main:main &&
+	check_push_result testrepo $the_commit heads/main
 
 '
 
 test_expect_success 'push with no ambiguity (2)' '
 
-	mk_test testrepo remotes/origin/master &&
-	git push testrepo master:origin/master &&
-	check_push_result testrepo $the_commit remotes/origin/master
+	mk_test testrepo remotes/origin/main &&
+	git push testrepo main:origin/main &&
+	check_push_result testrepo $the_commit remotes/origin/main
 
 '
 
 test_expect_success 'push with colon-less refspec, no ambiguity' '
 
-	mk_test testrepo heads/master heads/t/master &&
-	git branch -f t/master master &&
-	git push testrepo master &&
-	check_push_result testrepo $the_commit heads/master &&
-	check_push_result testrepo $the_first_commit heads/t/master
+	mk_test testrepo heads/main heads/t/main &&
+	git branch -f t/main main &&
+	git push testrepo main &&
+	check_push_result testrepo $the_commit heads/main &&
+	check_push_result testrepo $the_first_commit heads/t/main
 
 '
 
 test_expect_success 'push with weak ambiguity (1)' '
 
-	mk_test testrepo heads/master remotes/origin/master &&
-	git push testrepo master:master &&
-	check_push_result testrepo $the_commit heads/master &&
-	check_push_result testrepo $the_first_commit remotes/origin/master
+	mk_test testrepo heads/main remotes/origin/main &&
+	git push testrepo main:main &&
+	check_push_result testrepo $the_commit heads/main &&
+	check_push_result testrepo $the_first_commit remotes/origin/main
 
 '
 
 test_expect_success 'push with weak ambiguity (2)' '
 
-	mk_test testrepo heads/master remotes/origin/master remotes/another/master &&
-	git push testrepo master:master &&
-	check_push_result testrepo $the_commit heads/master &&
-	check_push_result testrepo $the_first_commit remotes/origin/master remotes/another/master
+	mk_test testrepo heads/main remotes/origin/main remotes/another/main &&
+	git push testrepo main:main &&
+	check_push_result testrepo $the_commit heads/main &&
+	check_push_result testrepo $the_first_commit remotes/origin/main remotes/another/main
 
 '
 
 test_expect_success 'push with ambiguity' '
 
 	mk_test testrepo heads/frotz tags/frotz &&
-	test_must_fail git push testrepo master:frotz &&
+	test_must_fail git push testrepo main:frotz &&
 	check_push_result testrepo $the_first_commit heads/frotz tags/frotz
 
 '
@@ -357,7 +360,7 @@
 test_expect_success 'push with colon-less refspec (1)' '
 
 	mk_test testrepo heads/frotz tags/frotz &&
-	git branch -f frotz master &&
+	git branch -f frotz main &&
 	git push testrepo frotz &&
 	check_push_result testrepo $the_commit heads/frotz &&
 	check_push_result testrepo $the_first_commit tags/frotz
@@ -385,7 +388,7 @@
 	then
 		git tag -d frotz
 	fi &&
-	git branch -f frotz master &&
+	git branch -f frotz main &&
 	git push testrepo frotz &&
 	check_push_result testrepo $the_commit heads/frotz &&
 	test 1 = $( cd testrepo && git show-ref | wc -l )
@@ -408,7 +411,7 @@
 test_expect_success 'push head with non-existent, incomplete dest' '
 
 	mk_test testrepo &&
-	git push testrepo master:branch &&
+	git push testrepo main:branch &&
 	check_push_result testrepo $the_commit heads/branch
 
 '
@@ -425,14 +428,14 @@
 test_expect_success 'push sha1 with non-existent, incomplete dest' '
 
 	mk_test testrepo &&
-	test_must_fail git push testrepo $(git rev-parse master):foo
+	test_must_fail git push testrepo $(git rev-parse main):foo
 
 '
 
 test_expect_success 'push ref expression with non-existent, incomplete dest' '
 
 	mk_test testrepo &&
-	test_must_fail git push testrepo master^:branch
+	test_must_fail git push testrepo main^:branch
 
 '
 
@@ -440,30 +443,26 @@
 do
 
 	test_expect_success "push with $head" '
-
-		mk_test testrepo heads/master &&
-		git checkout master &&
+		mk_test testrepo heads/main &&
+		git checkout main &&
 		git push testrepo $head &&
-		check_push_result testrepo $the_commit heads/master
-
+		check_push_result testrepo $the_commit heads/main
 	'
 
 	test_expect_success "push with $head nonexisting at remote" '
-
-		mk_test testrepo heads/master &&
-		git checkout -b local master &&
-		test_when_finished "git checkout master; git branch -D local" &&
+		mk_test testrepo heads/main &&
+		git checkout -b local main &&
+		test_when_finished "git checkout main; git branch -D local" &&
 		git push testrepo $head &&
 		check_push_result testrepo $the_commit heads/local
 	'
 
 	test_expect_success "push with +$head" '
-
-		mk_test testrepo heads/master &&
-		git checkout -b local master &&
-		test_when_finished "git checkout master; git branch -D local" &&
-		git push testrepo master local &&
-		check_push_result testrepo $the_commit heads/master &&
+		mk_test testrepo heads/main &&
+		git checkout -b local main &&
+		test_when_finished "git checkout main; git branch -D local" &&
+		git push testrepo main local &&
+		check_push_result testrepo $the_commit heads/main &&
 		check_push_result testrepo $the_commit heads/local &&
 
 		# Without force rewinding should fail
@@ -474,22 +473,19 @@
 		# With force rewinding should succeed
 		git push testrepo +$head &&
 		check_push_result testrepo $the_first_commit heads/local
-
 	'
 
 	test_expect_success "push $head with non-existent, incomplete dest" '
-
 		mk_test testrepo &&
-		git checkout master &&
+		git checkout main &&
 		git push testrepo $head:branch &&
 		check_push_result testrepo $the_commit heads/branch
 
 	'
 
 	test_expect_success "push with config remote.*.push = $head" '
-
 		mk_test testrepo heads/local &&
-		git checkout master &&
+		git checkout main &&
 		git branch -f local $the_commit &&
 		test_when_finished "git branch -D local" &&
 		(
@@ -499,168 +495,168 @@
 		) &&
 		test_config remote.there.url testrepo &&
 		test_config remote.there.push $head &&
-		test_config branch.master.remote there &&
+		test_config branch.main.remote there &&
 		git push &&
-		check_push_result testrepo $the_commit heads/master &&
+		check_push_result testrepo $the_commit heads/main &&
 		check_push_result testrepo $the_first_commit heads/local
 	'
 
 done
 
 test_expect_success 'push with remote.pushdefault' '
-	mk_test up_repo heads/master &&
-	mk_test down_repo heads/master &&
+	mk_test up_repo heads/main &&
+	mk_test down_repo heads/main &&
 	test_config remote.up.url up_repo &&
 	test_config remote.down.url down_repo &&
-	test_config branch.master.remote up &&
+	test_config branch.main.remote up &&
 	test_config remote.pushdefault down &&
 	test_config push.default matching &&
 	git push &&
-	check_push_result up_repo $the_first_commit heads/master &&
-	check_push_result down_repo $the_commit heads/master
+	check_push_result up_repo $the_first_commit heads/main &&
+	check_push_result down_repo $the_commit heads/main
 '
 
 test_expect_success 'push with config remote.*.pushurl' '
 
-	mk_test testrepo heads/master &&
-	git checkout master &&
+	mk_test testrepo heads/main &&
+	git checkout main &&
 	test_config remote.there.url test2repo &&
 	test_config remote.there.pushurl testrepo &&
 	git push there : &&
-	check_push_result testrepo $the_commit heads/master
+	check_push_result testrepo $the_commit heads/main
 '
 
 test_expect_success 'push with config branch.*.pushremote' '
-	mk_test up_repo heads/master &&
-	mk_test side_repo heads/master &&
-	mk_test down_repo heads/master &&
+	mk_test up_repo heads/main &&
+	mk_test side_repo heads/main &&
+	mk_test down_repo heads/main &&
 	test_config remote.up.url up_repo &&
 	test_config remote.pushdefault side_repo &&
 	test_config remote.down.url down_repo &&
-	test_config branch.master.remote up &&
-	test_config branch.master.pushremote down &&
+	test_config branch.main.remote up &&
+	test_config branch.main.pushremote down &&
 	test_config push.default matching &&
 	git push &&
-	check_push_result up_repo $the_first_commit heads/master &&
-	check_push_result side_repo $the_first_commit heads/master &&
-	check_push_result down_repo $the_commit heads/master
+	check_push_result up_repo $the_first_commit heads/main &&
+	check_push_result side_repo $the_first_commit heads/main &&
+	check_push_result down_repo $the_commit heads/main
 '
 
 test_expect_success 'branch.*.pushremote config order is irrelevant' '
-	mk_test one_repo heads/master &&
-	mk_test two_repo heads/master &&
+	mk_test one_repo heads/main &&
+	mk_test two_repo heads/main &&
 	test_config remote.one.url one_repo &&
 	test_config remote.two.url two_repo &&
-	test_config branch.master.pushremote two_repo &&
+	test_config branch.main.pushremote two_repo &&
 	test_config remote.pushdefault one_repo &&
 	test_config push.default matching &&
 	git push &&
-	check_push_result one_repo $the_first_commit heads/master &&
-	check_push_result two_repo $the_commit heads/master
+	check_push_result one_repo $the_first_commit heads/main &&
+	check_push_result two_repo $the_commit heads/main
 '
 
 test_expect_success 'push with dry-run' '
 
-	mk_test testrepo heads/master &&
-	old_commit=$(git -C testrepo show-ref -s --verify refs/heads/master) &&
+	mk_test testrepo heads/main &&
+	old_commit=$(git -C testrepo show-ref -s --verify refs/heads/main) &&
 	git push --dry-run testrepo : &&
-	check_push_result testrepo $old_commit heads/master
+	check_push_result testrepo $old_commit heads/main
 '
 
 test_expect_success 'push updates local refs' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child &&
 	(
 		cd child &&
-		git pull .. master &&
+		git pull .. main &&
 		git push &&
-		test $(git rev-parse master) = \
-			$(git rev-parse remotes/origin/master)
+		test $(git rev-parse main) = \
+			$(git rev-parse remotes/origin/main)
 	)
 
 '
 
 test_expect_success 'push updates up-to-date local refs' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child1 &&
 	mk_child testrepo child2 &&
-	(cd child1 && git pull .. master && git push) &&
+	(cd child1 && git pull .. main && git push) &&
 	(
 		cd child2 &&
-		git pull ../child1 master &&
+		git pull ../child1 main &&
 		git push &&
-		test $(git rev-parse master) = \
-			$(git rev-parse remotes/origin/master)
+		test $(git rev-parse main) = \
+			$(git rev-parse remotes/origin/main)
 	)
 
 '
 
 test_expect_success 'push preserves up-to-date packed refs' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child &&
 	(
 		cd child &&
 		git push &&
-		! test -f .git/refs/remotes/origin/master
+		! test -f .git/refs/remotes/origin/main
 	)
 
 '
 
 test_expect_success 'push does not update local refs on failure' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child &&
 	mkdir testrepo/.git/hooks &&
 	echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
 	chmod +x testrepo/.git/hooks/pre-receive &&
 	(
 		cd child &&
-		git pull .. master &&
+		git pull .. main &&
 		test_must_fail git push &&
-		test $(git rev-parse master) != \
-			$(git rev-parse remotes/origin/master)
+		test $(git rev-parse main) != \
+			$(git rev-parse remotes/origin/main)
 	)
 
 '
 
 test_expect_success 'allow deleting an invalid remote ref' '
 
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -f testrepo/.git/objects/??/* &&
-	git push testrepo :refs/heads/master &&
-	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+	git push testrepo :refs/heads/main &&
+	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/main)
 
 '
 
 test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' '
-	mk_test_with_hooks testrepo heads/master heads/next &&
-	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
-	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	mk_test_with_hooks testrepo heads/main heads/next &&
+	orgmain=$(cd testrepo && git show-ref -s --verify refs/heads/main) &&
+	newmain=$(git show-ref -s --verify refs/heads/main) &&
 	orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
 	newnext=$ZERO_OID &&
-	git push testrepo refs/heads/master:refs/heads/master :refs/heads/next &&
+	git push testrepo refs/heads/main:refs/heads/main :refs/heads/next &&
 	(
 		cd testrepo/.git &&
 		cat >pre-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		$orgnext $newnext refs/heads/next
 		EOF
 
 		cat >update.expect <<-EOF &&
-		refs/heads/master $orgmaster $newmaster
+		refs/heads/main $orgmain $newmain
 		refs/heads/next $orgnext $newnext
 		EOF
 
 		cat >post-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		$orgnext $newnext refs/heads/next
 		EOF
 
 		cat >post-update.expect <<-EOF &&
-		refs/heads/master
+		refs/heads/main
 		refs/heads/next
 		EOF
 
@@ -672,25 +668,25 @@
 '
 
 test_expect_success 'deleting dangling ref triggers hooks with correct args' '
-	mk_test_with_hooks testrepo heads/master &&
+	mk_test_with_hooks testrepo heads/main &&
 	rm -f testrepo/.git/objects/??/* &&
-	git push testrepo :refs/heads/master &&
+	git push testrepo :refs/heads/main &&
 	(
 		cd testrepo/.git &&
 		cat >pre-receive.expect <<-EOF &&
-		$ZERO_OID $ZERO_OID refs/heads/master
+		$ZERO_OID $ZERO_OID refs/heads/main
 		EOF
 
 		cat >update.expect <<-EOF &&
-		refs/heads/master $ZERO_OID $ZERO_OID
+		refs/heads/main $ZERO_OID $ZERO_OID
 		EOF
 
 		cat >post-receive.expect <<-EOF &&
-		$ZERO_OID $ZERO_OID refs/heads/master
+		$ZERO_OID $ZERO_OID refs/heads/main
 		EOF
 
 		cat >post-update.expect <<-EOF &&
-		refs/heads/master
+		refs/heads/main
 		EOF
 
 		test_cmp pre-receive.expect pre-receive.actual &&
@@ -701,28 +697,28 @@
 '
 
 test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' '
-	mk_test_with_hooks testrepo heads/master &&
-	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
-	newmaster=$(git show-ref -s --verify refs/heads/master) &&
-	git push testrepo master :refs/heads/nonexistent &&
+	mk_test_with_hooks testrepo heads/main &&
+	orgmain=$(cd testrepo && git show-ref -s --verify refs/heads/main) &&
+	newmain=$(git show-ref -s --verify refs/heads/main) &&
+	git push testrepo main :refs/heads/nonexistent &&
 	(
 		cd testrepo/.git &&
 		cat >pre-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		$ZERO_OID $ZERO_OID refs/heads/nonexistent
 		EOF
 
 		cat >update.expect <<-EOF &&
-		refs/heads/master $orgmaster $newmaster
+		refs/heads/main $orgmain $newmain
 		refs/heads/nonexistent $ZERO_OID $ZERO_OID
 		EOF
 
 		cat >post-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		EOF
 
 		cat >post-update.expect <<-EOF &&
-		refs/heads/master
+		refs/heads/main
 		EOF
 
 		test_cmp pre-receive.expect pre-receive.actual &&
@@ -733,7 +729,7 @@
 '
 
 test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' '
-	mk_test_with_hooks testrepo heads/master &&
+	mk_test_with_hooks testrepo heads/main &&
 	git push testrepo :refs/heads/nonexistent &&
 	(
 		cd testrepo/.git &&
@@ -753,40 +749,40 @@
 '
 
 test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' '
-	mk_test_with_hooks testrepo heads/master heads/next heads/seen &&
-	orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) &&
-	newmaster=$(git show-ref -s --verify refs/heads/master) &&
+	mk_test_with_hooks testrepo heads/main heads/next heads/seen &&
+	orgmain=$(cd testrepo && git show-ref -s --verify refs/heads/main) &&
+	newmain=$(git show-ref -s --verify refs/heads/main) &&
 	orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) &&
 	newnext=$ZERO_OID &&
 	orgseen=$(cd testrepo && git show-ref -s --verify refs/heads/seen) &&
-	newseen=$(git show-ref -s --verify refs/heads/master) &&
-	git push testrepo refs/heads/master:refs/heads/master \
-	    refs/heads/master:refs/heads/seen :refs/heads/next \
+	newseen=$(git show-ref -s --verify refs/heads/main) &&
+	git push testrepo refs/heads/main:refs/heads/main \
+	    refs/heads/main:refs/heads/seen :refs/heads/next \
 	    :refs/heads/nonexistent &&
 	(
 		cd testrepo/.git &&
 		cat >pre-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		$orgnext $newnext refs/heads/next
 		$orgseen $newseen refs/heads/seen
 		$ZERO_OID $ZERO_OID refs/heads/nonexistent
 		EOF
 
 		cat >update.expect <<-EOF &&
-		refs/heads/master $orgmaster $newmaster
+		refs/heads/main $orgmain $newmain
 		refs/heads/next $orgnext $newnext
 		refs/heads/seen $orgseen $newseen
 		refs/heads/nonexistent $ZERO_OID $ZERO_OID
 		EOF
 
 		cat >post-receive.expect <<-EOF &&
-		$orgmaster $newmaster refs/heads/master
+		$orgmain $newmain refs/heads/main
 		$orgnext $newnext refs/heads/next
 		$orgseen $newseen refs/heads/seen
 		EOF
 
 		cat >post-update.expect <<-EOF &&
-		refs/heads/master
+		refs/heads/main
 		refs/heads/next
 		refs/heads/seen
 		EOF
@@ -799,15 +795,15 @@
 '
 
 test_expect_success 'allow deleting a ref using --delete' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	(cd testrepo && git config receive.denyDeleteCurrent warn) &&
-	git push testrepo --delete master &&
-	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+	git push testrepo --delete main &&
+	(cd testrepo && test_must_fail git rev-parse --verify refs/heads/main)
 '
 
 test_expect_success 'allow deleting a tag using --delete' '
-	mk_test testrepo heads/master &&
-	git tag -a -m dummy_message deltag heads/master &&
+	mk_test testrepo heads/main &&
+	git tag -a -m dummy_message deltag heads/main &&
 	git push testrepo --tags &&
 	(cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
 	git push testrepo --delete tag deltag &&
@@ -815,56 +811,61 @@
 '
 
 test_expect_success 'push --delete without args aborts' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	test_must_fail git push testrepo --delete
 '
 
 test_expect_success 'push --delete refuses src:dest refspecs' '
+	mk_test testrepo heads/main &&
+	test_must_fail git push testrepo --delete main:foo
+'
+
+test_expect_success 'push --delete refuses empty string' '
 	mk_test testrepo heads/master &&
-	test_must_fail git push testrepo --delete master:foo
+	test_must_fail git push testrepo --delete ""
 '
 
 test_expect_success 'warn on push to HEAD of non-bare repository' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	(
 		cd testrepo &&
-		git checkout master &&
+		git checkout main &&
 		git config receive.denyCurrentBranch warn
 	) &&
-	git push testrepo master 2>stderr &&
+	git push testrepo main 2>stderr &&
 	grep "warning: updating the current branch" stderr
 '
 
 test_expect_success 'deny push to HEAD of non-bare repository' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	(
 		cd testrepo &&
-		git checkout master &&
+		git checkout main &&
 		git config receive.denyCurrentBranch true
 	) &&
-	test_must_fail git push testrepo master
+	test_must_fail git push testrepo main
 '
 
 test_expect_success 'allow push to HEAD of bare repository (bare)' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	(
 		cd testrepo &&
-		git checkout master &&
+		git checkout main &&
 		git config receive.denyCurrentBranch true &&
 		git config core.bare true
 	) &&
-	git push testrepo master 2>stderr &&
+	git push testrepo main 2>stderr &&
 	! grep "warning: updating the current branch" stderr
 '
 
 test_expect_success 'allow push to HEAD of non-bare repository (config)' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	(
 		cd testrepo &&
-		git checkout master &&
+		git checkout main &&
 		git config receive.denyCurrentBranch false
 	) &&
-	git push testrepo master 2>stderr &&
+	git push testrepo main 2>stderr &&
 	! grep "warning: updating the current branch" stderr
 '
 
@@ -880,7 +881,7 @@
 		git for-each-ref refs/heads >actual &&
 		test_cmp expect actual
 	) &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'fetch with branches containing #' '
@@ -893,7 +894,7 @@
 		git for-each-ref refs/heads >actual &&
 		test_cmp expect actual
 	) &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'push with branches' '
@@ -903,7 +904,7 @@
 	git push branch1 &&
 	(
 		cd testrepo &&
-		echo "$the_first_commit commit	refs/heads/master" >expect &&
+		echo "$the_first_commit commit	refs/heads/main" >expect &&
 		git for-each-ref refs/heads >actual &&
 		test_cmp expect actual
 	)
@@ -919,11 +920,11 @@
 		git for-each-ref refs/heads >actual &&
 		test_cmp expect actual
 	) &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'push into aliased refs (consistent)' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child1 &&
 	mk_child testrepo child2 &&
 	(
@@ -945,7 +946,7 @@
 '
 
 test_expect_success 'push into aliased refs (inconsistent)' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	mk_child testrepo child1 &&
 	mk_child testrepo child2 &&
 	(
@@ -976,7 +977,7 @@
 	tag_args=$2
 
 	test_expect_success "force pushing required to update $tag_type_description" "
-		mk_test testrepo heads/master &&
+		mk_test testrepo heads/main &&
 		mk_child testrepo child1 &&
 		mk_child testrepo child2 &&
 		(
@@ -1022,7 +1023,7 @@
 	tag_args=$2
 
 	test_expect_success "fetch will not clobber an existing $tag_type_description without --force" "
-		mk_test testrepo heads/master &&
+		mk_test testrepo heads/main &&
 		mk_child testrepo child1 &&
 		mk_child testrepo child2 &&
 		(
@@ -1045,12 +1046,12 @@
 test_expect_success 'push --porcelain' '
 	mk_empty testrepo &&
 	echo >.git/foo  "To testrepo" &&
-	echo >>.git/foo "*	refs/heads/master:refs/remotes/origin/master	[new reference]"  &&
+	echo >>.git/foo "*	refs/heads/main:refs/remotes/origin/main	[new reference]"  &&
 	echo >>.git/foo "Done" &&
-	git push >.git/bar --porcelain  testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push >.git/bar --porcelain  testrepo refs/heads/main:refs/remotes/origin/main &&
 	(
 		cd testrepo &&
-		echo "$the_commit commit	refs/remotes/origin/master" >expect &&
+		echo "$the_commit commit	refs/remotes/origin/main" >expect &&
 		git for-each-ref refs/remotes/origin >actual &&
 		test_cmp expect actual
 	) &&
@@ -1059,52 +1060,52 @@
 
 test_expect_success 'push --porcelain bad url' '
 	mk_empty testrepo &&
-	test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
+	test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/main:refs/remotes/origin/main &&
 	! grep -q Done .git/bar
 '
 
 test_expect_success 'push --porcelain rejected' '
 	mk_empty testrepo &&
-	git push testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push testrepo refs/heads/main:refs/remotes/origin/main &&
 	(cd testrepo &&
-		git reset --hard origin/master^ &&
+		git reset --hard origin/main^ &&
 		git config receive.denyCurrentBranch true) &&
 
 	echo >.git/foo  "To testrepo"  &&
-	echo >>.git/foo "!	refs/heads/master:refs/heads/master	[remote rejected] (branch is currently checked out)" &&
+	echo >>.git/foo "!	refs/heads/main:refs/heads/main	[remote rejected] (branch is currently checked out)" &&
 	echo >>.git/foo "Done" &&
 
-	test_must_fail git push >.git/bar --porcelain  testrepo refs/heads/master:refs/heads/master &&
+	test_must_fail git push >.git/bar --porcelain  testrepo refs/heads/main:refs/heads/main &&
 	test_cmp .git/foo .git/bar
 '
 
 test_expect_success 'push --porcelain --dry-run rejected' '
 	mk_empty testrepo &&
-	git push testrepo refs/heads/master:refs/remotes/origin/master &&
+	git push testrepo refs/heads/main:refs/remotes/origin/main &&
 	(cd testrepo &&
-		git reset --hard origin/master &&
+		git reset --hard origin/main &&
 		git config receive.denyCurrentBranch true) &&
 
 	echo >.git/foo  "To testrepo"  &&
-	echo >>.git/foo "!	refs/heads/master^:refs/heads/master	[rejected] (non-fast-forward)" &&
+	echo >>.git/foo "!	refs/heads/main^:refs/heads/main	[rejected] (non-fast-forward)" &&
 	echo >>.git/foo "Done" &&
 
-	test_must_fail git push >.git/bar --porcelain  --dry-run testrepo refs/heads/master^:refs/heads/master &&
+	test_must_fail git push >.git/bar --porcelain  --dry-run testrepo refs/heads/main^:refs/heads/main &&
 	test_cmp .git/foo .git/bar
 '
 
 test_expect_success 'push --prune' '
-	mk_test testrepo heads/master heads/second heads/foo heads/bar &&
+	mk_test testrepo heads/main heads/second heads/foo heads/bar &&
 	git push --prune testrepo : &&
-	check_push_result testrepo $the_commit heads/master &&
+	check_push_result testrepo $the_commit heads/main &&
 	check_push_result testrepo $the_first_commit heads/second &&
 	! check_push_result testrepo $the_first_commit heads/foo heads/bar
 '
 
 test_expect_success 'push --prune refspec' '
-	mk_test testrepo tmp/master tmp/second tmp/foo tmp/bar &&
+	mk_test testrepo tmp/main tmp/second tmp/foo tmp/bar &&
 	git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
-	check_push_result testrepo $the_commit tmp/master &&
+	check_push_result testrepo $the_commit tmp/main &&
 	check_push_result testrepo $the_first_commit tmp/second &&
 	! check_push_result testrepo $the_first_commit tmp/foo tmp/bar
 '
@@ -1112,18 +1113,18 @@
 for configsection in transfer receive
 do
 	test_expect_success "push to update a ref hidden by $configsection.hiderefs" '
-		mk_test testrepo heads/master hidden/one hidden/two hidden/three &&
+		mk_test testrepo heads/main hidden/one hidden/two hidden/three &&
 		(
 			cd testrepo &&
 			git config $configsection.hiderefs refs/hidden
 		) &&
 
 		# push to unhidden ref succeeds normally
-		git push testrepo master:refs/heads/master &&
-		check_push_result testrepo $the_commit heads/master &&
+		git push testrepo main:refs/heads/main &&
+		check_push_result testrepo $the_commit heads/main &&
 
 		# push to update a hidden ref should fail
-		test_must_fail git push testrepo master:refs/hidden/one &&
+		test_must_fail git push testrepo main:refs/hidden/one &&
 		check_push_result testrepo $the_first_commit hidden/one &&
 
 		# push to delete a hidden ref should fail
@@ -1137,8 +1138,8 @@
 done
 
 test_expect_success 'fetch exact SHA1' '
-	mk_test testrepo heads/master hidden/one &&
-	git push testrepo master:refs/hidden/one &&
+	mk_test testrepo heads/main hidden/one &&
+	git push testrepo main:refs/hidden/one &&
 	(
 		cd testrepo &&
 		git config transfer.hiderefs refs/hidden
@@ -1169,7 +1170,7 @@
 			git config uploadpack.allowtipsha1inwant true
 		) &&
 
-		git fetch -v ../testrepo $the_commit:refs/heads/copy master:refs/heads/extra &&
+		git fetch -v ../testrepo $the_commit:refs/heads/copy main:refs/heads/extra &&
 		cat >expect <<-EOF &&
 		$the_commit
 		$the_first_commit
@@ -1183,8 +1184,8 @@
 '
 
 test_expect_success 'fetch exact SHA1 in protocol v2' '
-	mk_test testrepo heads/master hidden/one &&
-	git push testrepo master:refs/hidden/one &&
+	mk_test testrepo heads/main hidden/one &&
+	git push testrepo main:refs/hidden/one &&
 	git -C testrepo config transfer.hiderefs refs/hidden &&
 	check_push_result testrepo $the_commit hidden/one &&
 
@@ -1268,17 +1269,17 @@
 done
 
 test_expect_success 'fetch follows tags by default' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git tag -m "annotated" tag &&
 		git for-each-ref >tmp1 &&
 		(
 			cat tmp1
-			sed -n "s|refs/heads/master$|refs/remotes/origin/master|p" tmp1
+			sed -n "s|refs/heads/main$|refs/remotes/origin/main|p" tmp1
 		) |
 		sort -k 3 >../expect
 	) &&
@@ -1286,8 +1287,8 @@
 	(
 		cd dst &&
 		git remote add origin ../src &&
-		git config branch.master.remote origin &&
-		git config branch.master.merge refs/heads/master &&
+		git config branch.main.remote origin &&
+		git config branch.main.merge refs/heads/main &&
 		git pull &&
 		git for-each-ref >../actual
 	) &&
@@ -1306,74 +1307,74 @@
 '
 
 test_expect_success 'pushing a specific ref applies remote.$name.push as refmap' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	git init --bare dst &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git branch next &&
 		git config remote.dst.url ../dst &&
 		git config remote.dst.push "+refs/heads/*:refs/remotes/src/*" &&
-		git push dst master &&
-		git show-ref refs/heads/master |
+		git push dst main &&
+		git show-ref refs/heads/main |
 		sed -e "s|refs/heads/|refs/remotes/src/|" >../dst/expect
 	) &&
 	(
 		cd dst &&
 		test_must_fail git show-ref refs/heads/next &&
-		test_must_fail git show-ref refs/heads/master &&
-		git show-ref refs/remotes/src/master >actual
+		test_must_fail git show-ref refs/heads/main &&
+		git show-ref refs/remotes/src/main >actual
 	) &&
 	test_cmp dst/expect dst/actual
 '
 
 test_expect_success 'with no remote.$name.push, it is not used as refmap' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	git init --bare dst &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git branch next &&
 		git config remote.dst.url ../dst &&
 		git config push.default matching &&
-		git push dst master &&
-		git show-ref refs/heads/master >../dst/expect
+		git push dst main &&
+		git show-ref refs/heads/main >../dst/expect
 	) &&
 	(
 		cd dst &&
 		test_must_fail git show-ref refs/heads/next &&
-		git show-ref refs/heads/master >actual
+		git show-ref refs/heads/main >actual
 	) &&
 	test_cmp dst/expect dst/actual
 '
 
 test_expect_success 'with no remote.$name.push, upstream mapping is used' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	git init --bare dst &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git branch next &&
 		git config remote.dst.url ../dst &&
 		git config remote.dst.fetch "+refs/heads/*:refs/remotes/dst/*" &&
 		git config push.default upstream &&
 
-		git config branch.master.merge refs/heads/trunk &&
-		git config branch.master.remote dst &&
+		git config branch.main.merge refs/heads/trunk &&
+		git config branch.main.remote dst &&
 
-		git push dst master &&
-		git show-ref refs/heads/master |
-		sed -e "s|refs/heads/master|refs/heads/trunk|" >../dst/expect
+		git push dst main &&
+		git show-ref refs/heads/main |
+		sed -e "s|refs/heads/main|refs/heads/trunk|" >../dst/expect
 	) &&
 	(
 		cd dst &&
-		test_must_fail git show-ref refs/heads/master &&
+		test_must_fail git show-ref refs/heads/main &&
 		test_must_fail git show-ref refs/heads/next &&
 		git show-ref refs/heads/trunk >actual
 	) &&
@@ -1381,20 +1382,20 @@
 '
 
 test_expect_success 'push does not follow tags by default' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	git init --bare dst &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git tag -m "annotated" tag &&
 		git checkout -b another &&
 		git commit --allow-empty -m "future commit" &&
 		git tag -m "future" future &&
-		git checkout master &&
-		git for-each-ref refs/heads/master >../expect &&
-		git push ../dst master
+		git checkout main &&
+		git for-each-ref refs/heads/main >../expect &&
+		git push ../dst main
 	) &&
 	(
 		cd dst &&
@@ -1404,20 +1405,20 @@
 '
 
 test_expect_success 'push --follow-tags only pushes relevant tags' '
-	mk_test testrepo heads/master &&
+	mk_test testrepo heads/main &&
 	rm -fr src dst &&
 	git init src &&
 	git init --bare dst &&
 	(
 		cd src &&
-		git pull ../testrepo master &&
+		git pull ../testrepo main &&
 		git tag -m "annotated" tag &&
 		git checkout -b another &&
 		git commit --allow-empty -m "future commit" &&
 		git tag -m "future" future &&
-		git checkout master &&
-		git for-each-ref refs/heads/master refs/tags/tag >../expect &&
-		git push --follow-tags ../dst master
+		git checkout main &&
+		git for-each-ref refs/heads/main refs/tags/tag >../expect &&
+		git push --follow-tags ../dst main
 	) &&
 	(
 		cd dst &&
@@ -1435,12 +1436,12 @@
 	git commit -am initial &&
 	git init no-thin &&
 	git --git-dir=no-thin/.git config receive.unpacklimit 0 &&
-	git push no-thin/.git refs/heads/master:refs/heads/foo &&
+	git push no-thin/.git refs/heads/main:refs/heads/foo &&
 	echo modified >> path1 &&
 	git commit -am modified &&
 	git repack -adf &&
 	rcvpck="git receive-pack --reject-thin-pack-for-testing" &&
-	git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
+	git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/main:refs/heads/foo
 '
 
 test_expect_success 'pushing a tag pushes the tagged object' '
@@ -1463,12 +1464,12 @@
 
 	# double push to test both with and without
 	# the actual pack transfer
-	git push dst.git master:one &&
+	git push dst.git main:one &&
 	echo "one@{0} push" >expect &&
 	git -C dst.git log -g --format="%gd %gs" one >actual &&
 	test_cmp expect actual &&
 
-	git push dst.git master:two &&
+	git push dst.git main:two &&
 	echo "two@{0} push" >expect &&
 	git -C dst.git log -g --format="%gd %gs" two >actual &&
 	test_cmp expect actual
@@ -1483,20 +1484,20 @@
 
 		# as above, we double-fetch to test both
 		# with and without pack transfer
-		git fetch .. master:one &&
-		echo "one@{0} fetch .. master:one: storing head" >expect &&
+		git fetch .. main:one &&
+		echo "one@{0} fetch .. main:one: storing head" >expect &&
 		git log -g --format="%gd %gs" one >actual &&
 		test_cmp expect actual &&
 
-		git fetch .. master:two &&
-		echo "two@{0} fetch .. master:two: storing head" >expect &&
+		git fetch .. main:two &&
+		echo "two@{0} fetch .. main:two: storing head" >expect &&
 		git log -g --format="%gd %gs" two >actual &&
 		test_cmp expect actual
 	)
 '
 
 test_expect_success 'receive.denyCurrentBranch = updateInstead' '
-	git push testrepo master &&
+	git push testrepo main &&
 	(
 		cd testrepo &&
 		git reset --hard &&
@@ -1505,7 +1506,7 @@
 	test_commit third path2 &&
 
 	# Try pushing into a repository with pristine working tree
-	git push testrepo master &&
+	git push testrepo main &&
 	(
 		cd testrepo &&
 		git update-index -q --refresh &&
@@ -1522,7 +1523,7 @@
 		test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
 		test-tool chmtime +100 path1
 	) &&
-	git push testrepo master &&
+	git push testrepo main &&
 	(
 		cd testrepo &&
 		git update-index -q --refresh &&
@@ -1542,7 +1543,7 @@
 		cd testrepo &&
 		echo changed >path1
 	) &&
-	test_must_fail git push testrepo master &&
+	test_must_fail git push testrepo main &&
 	(
 		cd testrepo &&
 		test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
@@ -1556,7 +1557,7 @@
 		echo changed >path1 &&
 		git add path1
 	) &&
-	test_must_fail git push testrepo master &&
+	test_must_fail git push testrepo main &&
 	(
 		cd testrepo &&
 		test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
@@ -1573,7 +1574,7 @@
 		git reset --hard &&
 		echo changed >path3
 	) &&
-	test_must_fail git push testrepo master &&
+	test_must_fail git push testrepo main &&
 	(
 		cd testrepo &&
 		test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
@@ -1589,7 +1590,7 @@
 		echo fifth >path3 &&
 		git add path3
 	) &&
-	test_must_fail git push testrepo master &&
+	test_must_fail git push testrepo main &&
 	(
 		cd testrepo &&
 		test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
@@ -1604,17 +1605,17 @@
 		cd void &&
 		git config receive.denyCurrentBranch updateInstead
 	) &&
-	git push void master &&
+	git push void main &&
 	(
 		cd void &&
-		test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
+		test $(git -C .. rev-parse main) = $(git rev-parse HEAD) &&
 		git diff --quiet &&
 		git diff --cached --quiet
 	) &&
 
 	# (6) updateInstead intervened by fast-forward check
-	test_must_fail git push void master^:master &&
-	test $(git -C void rev-parse HEAD) = $(git rev-parse master) &&
+	test_must_fail git push void main^:main &&
+	test $(git -C void rev-parse HEAD) = $(git rev-parse main) &&
 	git -C void diff --quiet &&
 	git -C void diff --cached --quiet
 '
@@ -1624,7 +1625,7 @@
 	git init testrepo &&
 	(
 		cd testrepo &&
-		git pull .. master &&
+		git pull .. main &&
 		git reset --hard HEAD^^ &&
 		git tag initial &&
 		git config receive.denyCurrentBranch updateInstead &&
@@ -1642,7 +1643,7 @@
 	) &&
 
 	# Try pushing into a pristine
-	git push testrepo master &&
+	git push testrepo main &&
 	(
 		cd testrepo &&
 		git diff --quiet &&
@@ -1656,7 +1657,7 @@
 		git reset --hard initial &&
 		echo conflicting >path2
 	) &&
-	test_must_fail git push testrepo master &&
+	test_must_fail git push testrepo main &&
 	(
 		cd testrepo &&
 		test $(git rev-parse initial) = $(git rev-parse HEAD) &&
@@ -1672,7 +1673,7 @@
 		echo irrelevant >path5 &&
 		git add path5
 	) &&
-	git push testrepo master &&
+	git push testrepo main &&
 	(
 		cd testrepo &&
 		test "$(cat path1)" = unrelated &&
@@ -1712,7 +1713,7 @@
 		EOF
 	) &&
 
-	git push void master &&
+	git push void main &&
 	(
 		cd void &&
 		git diff --quiet &&
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
index e4edd56..a448e16 100755
--- a/t/t5517-push-mirror.sh
+++ b/t/t5517-push-mirror.sh
@@ -2,6 +2,9 @@
 
 test_description='pushing to a mirror repository'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 D=$(pwd)
@@ -15,16 +18,16 @@
 }
 
 mk_repo_pair () {
-	rm -rf master mirror &&
+	rm -rf main mirror &&
 	mkdir mirror &&
 	(
 		cd mirror &&
 		git init &&
 		git config receive.denyCurrentBranch warn
 	) &&
-	mkdir master &&
+	mkdir main &&
 	(
-		cd master &&
+		cd main &&
 		git init &&
 		git remote add $1 up ../mirror
 	)
@@ -36,13 +39,13 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/heads/main) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/heads/main) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -50,15 +53,15 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
 		git push --mirror up &&
 		echo two >foo && git add foo && git commit -m two &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/heads/main) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/heads/main) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -66,7 +69,7 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
 		git push --mirror up &&
 		echo two >foo && git add foo && git commit -m two &&
@@ -74,9 +77,9 @@
 		git reset --hard HEAD^ &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/heads/main) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/heads/main) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -84,9 +87,9 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git branch remove master &&
+		git branch remove main &&
 		git push --mirror up &&
 		git branch -D remove &&
 		git push --mirror up
@@ -102,21 +105,21 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git branch remove master &&
+		git branch remove main &&
 		git push --mirror up &&
 		git branch -D remove &&
-		git branch add master &&
+		git branch add main &&
 		echo two >foo && git add foo && git commit -m two &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
-	master_add=$(cd master && git show-ref -s --verify refs/heads/add) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+	main_main=$(cd main && git show-ref -s --verify refs/heads/main) &&
+	main_add=$(cd main && git show-ref -s --verify refs/heads/add) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/heads/main) &&
 	mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) &&
-	test "$master_master" = "$mirror_master" &&
-	test "$master_add" = "$mirror_add" &&
+	test "$main_main" = "$mirror_main" &&
+	test "$main_add" = "$mirror_add" &&
 	(
 		cd mirror &&
 		invert git show-ref -s --verify refs/heads/remove
@@ -130,14 +133,14 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/tags/tmain) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/tags/tmain) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -145,17 +148,17 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up &&
 		echo two >foo && git add foo && git commit -m two &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/tags/tmain) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/tags/tmain) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -163,20 +166,20 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up &&
 		echo two >foo && git add foo && git commit -m two &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up &&
 		git reset --hard HEAD^ &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
-	test "$master_master" = "$mirror_master"
+	main_main=$(cd main && git show-ref -s --verify refs/tags/tmain) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/tags/tmain) &&
+	test "$main_main" = "$mirror_main"
 
 '
 
@@ -184,9 +187,9 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git tag -f tremove master &&
+		git tag -f tremove main &&
 		git push --mirror up &&
 		git tag -d tremove &&
 		git push --mirror up
@@ -202,23 +205,23 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git tag -f tmaster master &&
-		git tag -f tremove master &&
+		git tag -f tmain main &&
+		git tag -f tremove main &&
 		git push --mirror up &&
 		git tag -d tremove &&
-		git tag tadd master &&
+		git tag tadd main &&
 		echo two >foo && git add foo && git commit -m two &&
-		git tag -f tmaster master &&
+		git tag -f tmain main &&
 		git push --mirror up
 	) &&
-	master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
-	master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) &&
-	mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+	main_main=$(cd main && git show-ref -s --verify refs/tags/tmain) &&
+	main_add=$(cd main && git show-ref -s --verify refs/tags/tadd) &&
+	mirror_main=$(cd mirror && git show-ref -s --verify refs/tags/tmain) &&
 	mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) &&
-	test "$master_master" = "$mirror_master" &&
-	test "$master_add" = "$mirror_add" &&
+	test "$main_main" = "$mirror_main" &&
+	test "$main_add" = "$mirror_add" &&
 	(
 		cd mirror &&
 		invert git show-ref -s --verify refs/tags/tremove
@@ -230,10 +233,10 @@
 
 	mk_repo_pair --mirror &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
-		git branch keep master &&
-		git branch remove master &&
+		git branch keep main &&
+		git branch remove main &&
 		git push up &&
 		git branch -D remove &&
 		git push up
@@ -250,10 +253,10 @@
 
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
 		git config --add remote.up.mirror no &&
-		git branch keep master &&
+		git branch keep main &&
 		git push --mirror up &&
 		git branch -D keep &&
 		git push up :
@@ -268,10 +271,10 @@
 test_expect_success 'push to mirrored repository with refspec fails' '
 	mk_repo_pair &&
 	(
-		cd master &&
+		cd main &&
 		echo one >foo && git add foo && git commit -m one &&
 		git config --add remote.up.mirror true &&
-		test_must_fail git push up master
+		test_must_fail git push up main
 	)
 '
 
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index c2060bb..5c4ac25 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -5,6 +5,9 @@
 
 test_description='fetch exit status test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -17,20 +20,20 @@
 	echo side >file &&
 	git commit -a -m side &&
 
-	git checkout master &&
+	git checkout main &&
 	echo next >file &&
 	git commit -a -m next
 '
 
 test_expect_success 'non-fast-forward fetch' '
 
-	test_must_fail git fetch . master:side
+	test_must_fail git fetch . main:side
 
 '
 
 test_expect_success 'forced update' '
 
-	git fetch . +master:side
+	git fetch . +main:side
 
 '
 
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
index 11fcd37..20ba604 100755
--- a/t/t5519-push-alternates.sh
+++ b/t/t5519-push-alternates.sh
@@ -2,6 +2,9 @@
 
 test_description='push to a repository that borrows from elsewhere'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -17,7 +20,7 @@
 		>file &&
 		git add . &&
 		git commit -m initial &&
-		git push ../alice-pub master
+		git push ../alice-pub main
 	) &&
 
 	# Project Bob is a fork of project Alice
@@ -31,7 +34,7 @@
 	git clone alice-pub bob-work &&
 	(
 		cd bob-work &&
-		git push ../bob-pub master
+		git push ../bob-pub main
 	)
 '
 
@@ -54,7 +57,7 @@
 		# has at her public repository are available to it
 		# via its alternates.
 		cd bob-work &&
-		git pull ../alice-pub master &&
+		git pull ../alice-pub main &&
 		echo more bob >file &&
 		git commit -a -m third &&
 		git push ../bob-pub :
@@ -93,7 +96,7 @@
 test_expect_success 'bob works and pushes' '
 	(
 		# This time Bob does not pull from Alice, and
-		# the master branch at her public repository points
+		# the main branch at her public repository points
 		# at a commit Bob does not know about.  This should
 		# not prevent the push by Bob from succeeding.
 		cd bob-work &&
@@ -122,11 +125,11 @@
 test_expect_success 'bob works and pushes again' '
 	(
 		cd alice-pub &&
-		git cat-file commit master >../bob-work/commit
+		git cat-file commit main >../bob-work/commit
 	) &&
 	(
 		# This time Bob does not pull from Alice, and
-		# the master branch at her public repository points
+		# the main branch at her public repository points
 		# at a commit Bob does not fully know about, but
 		# he happens to have the commit object (but not the
 		# necessary tree) in his repository from Alice.
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 9fae07c..a094113 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -2,6 +2,9 @@
 
 test_description='pulling into void'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 modify () {
@@ -48,11 +51,11 @@
 	test_cmp file cloned/file
 '
 
-test_expect_success 'pulling into void using master:master' '
+test_expect_success 'pulling into void using main:main' '
 	git init cloned-uho &&
 	(
 		cd cloned-uho &&
-		git pull .. master:master
+		git pull .. main:main
 	) &&
 	test_path_is_file file &&
 	test_path_is_file cloned-uho/file &&
@@ -64,7 +67,7 @@
 	(
 		cd cloned-untracked &&
 		echo untracked >file &&
-		test_must_fail git pull .. master &&
+		test_must_fail git pull .. main &&
 		echo untracked >expect &&
 		test_cmp expect file
 	)
@@ -76,7 +79,7 @@
 		cd cloned-staged-colliding &&
 		echo "alternate content" >file &&
 		git add file &&
-		test_must_fail git pull .. master &&
+		test_must_fail git pull .. main &&
 		echo "alternate content" >expect &&
 		test_cmp expect file &&
 		git cat-file blob :file >file.index &&
@@ -90,7 +93,7 @@
 		cd cloned-staged-new &&
 		echo "new tracked file" >newfile &&
 		git add newfile &&
-		git pull .. master &&
+		git pull .. main &&
 		echo "new tracked file" >expect &&
 		test_cmp expect newfile &&
 		git cat-file blob :newfile >newfile.index &&
@@ -102,15 +105,15 @@
 	git init cloned-octopus &&
 	(
 		cd cloned-octopus &&
-		test_must_fail git pull .. master master &&
+		test_must_fail git pull .. main main &&
 		test_path_is_missing file
 	)
 '
 
 test_expect_success 'test . as a remote' '
-	git branch copy master &&
+	git branch copy main &&
 	git config branch.copy.remote . &&
-	git config branch.copy.merge refs/heads/master &&
+	git config branch.copy.merge refs/heads/main &&
 	echo updated >file &&
 	git commit -a -m updated &&
 	git checkout copy &&
@@ -126,7 +129,7 @@
 '
 
 test_expect_success 'the default remote . should not break explicit pull' '
-	git checkout -b second master^ &&
+	git checkout -b second main^ &&
 	echo modified >file &&
 	git commit -a -m modified &&
 	git checkout copy &&
@@ -361,7 +364,7 @@
 
 test_expect_success '--rebase fails with multiple branches' '
 	git reset --hard before-rebase &&
-	test_must_fail git pull --rebase . copy master 2>err &&
+	test_must_fail git pull --rebase . copy main 2>err &&
 	test_cmp_rev HEAD before-rebase &&
 	test_i18ngrep "Cannot rebase onto multiple branches" err &&
 	echo modified >expect &&
@@ -500,7 +503,7 @@
 	test_i18ngrep ! "verify-signatures" err
 '
 
-# add a feature branch, keep-merge, that is merged into master, so the
+# add a feature branch, keep-merge, that is merged into main, so the
 # test can try preserving the merge commit (or not) with various
 # --rebase flags/pull.rebase settings.
 test_expect_success 'preserve merge setup' '
@@ -699,12 +702,12 @@
 '
 
 test_expect_success 'pull --rebase works on branch yet to be born' '
-	git rev-parse master >expect &&
+	git rev-parse main >expect &&
 	mkdir empty_repo &&
 	(
 		cd empty_repo &&
 		git init &&
-		git pull --rebase .. master &&
+		git pull --rebase .. main &&
 		git rev-parse HEAD >../actual
 	) &&
 	test_cmp expect actual
@@ -720,7 +723,7 @@
 		echo staged-file >expect &&
 		git ls-files >actual &&
 		test_cmp expect actual &&
-		test_must_fail git pull --rebase .. master 2>err &&
+		test_must_fail git pull --rebase .. main 2>err &&
 		git ls-files >actual &&
 		test_cmp expect actual &&
 		git show :staged-file >actual &&
@@ -779,7 +782,7 @@
 	(
 		cd dst &&
 		test_might_fail git rebase --abort &&
-		git reset --hard origin/master
+		git reset --hard origin/main
 	) &&
 	git clone --bare src src-replace.git &&
 	rm -rf src &&
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index db1a381..63a688b 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -2,6 +2,9 @@
 
 test_description='pull options'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -99,13 +102,13 @@
 	cat >>.git/config <<-\EOF &&
 	[remote "one"]
 		url = ../parent
-		fetch = refs/heads/master:refs/heads/mirror
+		fetch = refs/heads/main:refs/heads/mirror
 	[remote "two"]
 		url = ../parent
-		fetch = refs/heads/master:refs/heads/origin
-	[branch "master"]
+		fetch = refs/heads/main:refs/heads/origin
+	[branch "main"]
 		remote = two
-		merge = refs/heads/master
+		merge = refs/heads/main
 	EOF
 	git pull two &&
 	test_commit A &&
@@ -124,9 +127,9 @@
 	[remote "two"]
 		url = ../parent
 		fetch = refs/heads/*:refs/remotes/two/*
-	[branch "master"]
+	[branch "main"]
 		remote = one
-		merge = refs/heads/master
+		merge = refs/heads/main
 	EOF
 	git pull --all
 	)
@@ -139,7 +142,7 @@
 		cd clonedry &&
 		git pull --dry-run ../parent &&
 		test_path_is_missing .git/FETCH_HEAD &&
-		test_path_is_missing .git/refs/heads/master &&
+		test_path_is_missing .git/refs/heads/main &&
 		test_path_is_missing .git/index &&
 		test_path_is_missing file
 	)
@@ -153,7 +156,7 @@
 		git remote add origin ../parent &&
 		git pull --all --dry-run &&
 		test_path_is_missing .git/FETCH_HEAD &&
-		test_path_is_missing .git/refs/remotes/origin/master &&
+		test_path_is_missing .git/refs/remotes/origin/main &&
 		test_path_is_missing .git/index &&
 		test_path_is_missing file
 	)
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index e47b5db..9fbe7f7 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='push with --set-upstream'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -26,19 +29,19 @@
 	test_cmp expect.$1 actual.$1
 }
 
-test_expect_success 'push -u master:master' '
-	git push -u upstream master:master &&
-	check_config master upstream refs/heads/master
+test_expect_success 'push -u main:main' '
+	git push -u upstream main:main &&
+	check_config main upstream refs/heads/main
 '
 
-test_expect_success 'push -u master:other' '
-	git push -u upstream master:other &&
-	check_config master upstream refs/heads/other
+test_expect_success 'push -u main:other' '
+	git push -u upstream main:other &&
+	check_config main upstream refs/heads/other
 '
 
-test_expect_success 'push -u --dry-run master:otherX' '
-	git push -u --dry-run upstream master:otherX &&
-	check_config master upstream refs/heads/other
+test_expect_success 'push -u --dry-run main:otherX' '
+	git push -u --dry-run upstream main:otherX &&
+	check_config main upstream refs/heads/other
 '
 
 test_expect_success 'push -u topic_2:topic_2' '
@@ -74,7 +77,7 @@
 test_expect_success TTY 'progress messages go to tty' '
 	ensure_fresh_upstream &&
 
-	test_terminal git push -u upstream master >out 2>err &&
+	test_terminal git push -u upstream main >out 2>err &&
 	test_i18ngrep "Writing objects" err
 '
 
@@ -82,7 +85,7 @@
 	ensure_fresh_upstream &&
 
 	# skip progress messages, since stderr is non-tty
-	git push -u upstream master >out 2>err &&
+	git push -u upstream main >out 2>err &&
 	test_i18ngrep ! "Writing objects" err
 '
 
@@ -90,21 +93,21 @@
 	ensure_fresh_upstream &&
 
 	# force progress messages to stderr, even though it is non-tty
-	git push -u --progress upstream master >out 2>err &&
+	git push -u --progress upstream main >out 2>err &&
 	test_i18ngrep "Writing objects" err
 '
 
 test_expect_success TTY 'push -q suppresses progress' '
 	ensure_fresh_upstream &&
 
-	test_terminal git push -u -q upstream master >out 2>err &&
+	test_terminal git push -u -q upstream main >out 2>err &&
 	test_i18ngrep ! "Writing objects" err
 '
 
 test_expect_success TTY 'push --no-progress suppresses progress' '
 	ensure_fresh_upstream &&
 
-	test_terminal git push -u --no-progress upstream master >out 2>err &&
+	test_terminal git push -u --no-progress upstream main >out 2>err &&
 	test_i18ngrep ! "Unpacking objects" err &&
 	test_i18ngrep ! "Writing objects" err
 '
@@ -112,7 +115,7 @@
 test_expect_success TTY 'quiet push' '
 	ensure_fresh_upstream &&
 
-	test_terminal git push --quiet --no-progress upstream master 2>&1 | tee output &&
+	test_terminal git push --quiet --no-progress upstream main 2>&1 | tee output &&
 	test_must_be_empty output
 '
 
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index 53d7b8e..ed11569 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -3,6 +3,9 @@
 
 test_description='Recursive "git fetch" for submodules'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 pwd=$(pwd)
@@ -71,7 +74,7 @@
 		git fetch --recurse-submodules >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "submodule.recurse option triggers recursive fetch" '
@@ -81,7 +84,7 @@
 		git -c submodule.recurse fetch >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
@@ -91,7 +94,7 @@
 		GIT_TRACE="$TRASH_DIRECTORY/trace.out" git fetch --recurse-submodules -j2 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err &&
+	test_cmp expect.err actual.err &&
 	grep "2 tasks" trace.out
 '
 
@@ -121,7 +124,7 @@
 		git fetch >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
@@ -152,7 +155,7 @@
 		git config --unset submodule.submodule.fetchRecurseSubmodules
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "--quiet propagates to submodules" '
@@ -180,7 +183,7 @@
 		git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "Without --dry-run propagates to submodules" '
@@ -189,7 +192,7 @@
 		git fetch --recurse-submodules >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "recurseSubmodules=true propagates into submodules" '
@@ -200,7 +203,7 @@
 		git fetch >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "--recurse-submodules overrides config in submodule" '
@@ -214,7 +217,7 @@
 		git fetch --recurse-submodules >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "--no-recurse-submodules overrides config setting" '
@@ -254,7 +257,7 @@
 		cd downstream &&
 		git fetch >../actual.out 2>../actual.err
 	) &&
-	test_i18ncmp expect.err.sub actual.err &&
+	test_cmp expect.err.sub actual.err &&
 	test_must_be_empty actual.out
 '
 
@@ -272,7 +275,7 @@
 		git fetch >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err.file actual.err
+	test_cmp expect.err.file actual.err
 '
 
 test_expect_success "Recursion picks up config in submodule" '
@@ -300,7 +303,7 @@
 			git config --unset fetch.recurseSubmodules
 		)
 	) &&
-	test_i18ncmp expect.err.sub actual.err &&
+	test_cmp expect.err.sub actual.err &&
 	test_must_be_empty actual.out
 '
 
@@ -333,7 +336,7 @@
 		cd downstream &&
 		git fetch >../actual.out 2>../actual.err
 	) &&
-	test_i18ncmp expect.err.2 actual.err &&
+	test_cmp expect.err.2 actual.err &&
 	test_must_be_empty actual.out
 '
 
@@ -389,7 +392,7 @@
 		)
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err
+	test_cmp expect.err actual.err
 '
 
 test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
@@ -406,7 +409,7 @@
 		git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err.file actual.err
+	test_cmp expect.err.file actual.err
 '
 
 test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
@@ -434,7 +437,7 @@
 		git config --unset fetch.recurseSubmodules
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err.2 actual.err
+	test_cmp expect.err.2 actual.err
 '
 
 test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
@@ -462,7 +465,7 @@
 		git config --unset submodule.submodule.fetchRecurseSubmodules
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err.2 actual.err
+	test_cmp expect.err.2 actual.err
 '
 
 test_expect_success "don't fetch submodule when newly recorded commits are already present" '
@@ -481,7 +484,7 @@
 		git fetch >../actual.out 2>../actual.err
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err actual.err &&
+	test_cmp expect.err actual.err &&
 	(
 		cd submodule &&
 		git checkout -q sub
@@ -517,7 +520,7 @@
 		git reset --hard
 	) &&
 	test_must_be_empty actual.out &&
-	test_i18ncmp expect.err.2 actual.err &&
+	test_cmp expect.err.2 actual.err &&
 	git checkout HEAD^ -- .gitmodules &&
 	git add .gitmodules &&
 	git commit -m "new submodule restored .gitmodules"
diff --git a/t/t5527-fetch-odd-refs.sh b/t/t5527-fetch-odd-refs.sh
index 3b0cb98..e2770e4 100755
--- a/t/t5527-fetch-odd-refs.sh
+++ b/t/t5527-fetch-odd-refs.sh
@@ -1,17 +1,20 @@
 #!/bin/sh
 
 test_description='test fetching of oddly-named refs'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # afterwards we will have:
 #  HEAD - two
-#  refs/for/refs/heads/master - one
-#  refs/heads/master - three
+#  refs/for/refs/heads/main - one
+#  refs/heads/main - three
 test_expect_success 'setup repo with odd suffix ref' '
 	echo content >file &&
 	git add . &&
 	git commit -m one &&
-	git update-ref refs/for/refs/heads/master HEAD &&
+	git update-ref refs/for/refs/heads/main HEAD &&
 	echo content >>file &&
 	git commit -a -m two &&
 	echo content >>file &&
@@ -22,7 +25,7 @@
 test_expect_success 'suffix ref is ignored during fetch' '
 	git clone --bare file://"$PWD" suffix &&
 	echo three >expect &&
-	git --git-dir=suffix log -1 --format=%s refs/heads/master >actual &&
+	git --git-dir=suffix log -1 --format=%s refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -33,7 +36,7 @@
 	(
 		cd long &&
 		test_commit long &&
-		test_commit master
+		test_commit main
 	) &&
 	if git -C long update-ref refs/heads/$ref1440 long; then
 		test_set_prereq LONG_REF
@@ -46,7 +49,7 @@
 	git fetch long refs/heads/*:refs/remotes/long/* &&
 	cat >expect <<-\EOF &&
 	long
-	master
+	main
 	EOF
 	git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
 	test_cmp expect actual
@@ -55,7 +58,7 @@
 test_expect_success LONG_REF 'push handles extremely long refname' '
 	git push long :refs/heads/$ref1440 &&
 	git -C long for-each-ref --format="%(subject)" refs/heads >actual &&
-	echo master >expect &&
+	echo main >expect &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index f0a287d..f280e00 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='check various push.default settings'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup bare remotes' '
@@ -41,7 +44,7 @@
 
 # $1 = success or failure
 # $2 = push.default value
-# $3 = branch to check for actual output (master or foo)
+# $3 = branch to check for actual output (main or foo)
 # $4 = [optional] switch to triangular workflow
 test_pushdefault_workflow () {
 	workflow=central
@@ -51,8 +54,8 @@
 		pushdefault=parent2
 	fi
 	test_expect_success "push.default = $2 $1 in $workflow workflows" "
-		test_config branch.master.remote parent1 &&
-		test_config branch.master.merge refs/heads/foo &&
+		test_config branch.main.remote parent1 &&
+		test_config branch.main.merge refs/heads/foo &&
 		test_config remote.pushdefault $pushdefault &&
 		test_commit commit-for-$2${4+-triangular} &&
 		test_push_$1 $2 $3 ${4+repo2}
@@ -60,32 +63,32 @@
 }
 
 test_expect_success '"upstream" pushes to configured upstream' '
-	git checkout master &&
-	test_config branch.master.remote parent1 &&
-	test_config branch.master.merge refs/heads/foo &&
+	git checkout main &&
+	test_config branch.main.remote parent1 &&
+	test_config branch.main.merge refs/heads/foo &&
 	test_commit two &&
 	test_push_success upstream foo
 '
 
 test_expect_success '"upstream" does not push on unconfigured remote' '
-	git checkout master &&
-	test_unconfig branch.master.remote &&
+	git checkout main &&
+	test_unconfig branch.main.remote &&
 	test_commit three &&
 	test_push_failure upstream
 '
 
 test_expect_success '"upstream" does not push on unconfigured branch' '
-	git checkout master &&
-	test_config branch.master.remote parent1 &&
-	test_unconfig branch.master.merge &&
+	git checkout main &&
+	test_config branch.main.remote parent1 &&
+	test_unconfig branch.main.merge &&
 	test_commit four &&
 	test_push_failure upstream
 '
 
 test_expect_success '"upstream" does not push when remotes do not match' '
-	git checkout master &&
-	test_config branch.master.remote parent1 &&
-	test_config branch.master.merge refs/heads/foo &&
+	git checkout main &&
+	test_config branch.main.remote parent1 &&
+	test_config branch.main.merge refs/heads/foo &&
 	test_config push.default upstream &&
 	test_commit five &&
 	test_must_fail git push parent2
@@ -121,99 +124,99 @@
 '
 
 test_expect_success 'push to existing branch, with no upstream configured' '
-	test_config branch.master.remote repo1 &&
-	git checkout master &&
+	test_config branch.main.remote repo1 &&
+	git checkout main &&
 	test_push_failure simple &&
 	test_push_failure upstream
 '
 
 test_expect_success 'push to existing branch, upstream configured with same name' '
-	test_config branch.master.remote repo1 &&
-	test_config branch.master.merge refs/heads/master &&
-	git checkout master &&
+	test_config branch.main.remote repo1 &&
+	test_config branch.main.merge refs/heads/main &&
+	git checkout main &&
 	test_commit six &&
-	test_push_success upstream master &&
+	test_push_success upstream main &&
 	test_commit seven &&
-	test_push_success simple master
+	test_push_success simple main
 '
 
 test_expect_success 'push to existing branch, upstream configured with different name' '
-	test_config branch.master.remote repo1 &&
-	test_config branch.master.merge refs/heads/other-name &&
-	git checkout master &&
+	test_config branch.main.remote repo1 &&
+	test_config branch.main.merge refs/heads/other-name &&
+	git checkout main &&
 	test_commit eight &&
 	test_push_success upstream other-name &&
 	test_commit nine &&
 	test_push_failure simple &&
 	git --git-dir=repo1 log -1 --format="%h %s" "other-name" >expect-other-name &&
-	test_push_success current master &&
+	test_push_success current main &&
 	git --git-dir=repo1 log -1 --format="%h %s" "other-name" >actual-other-name &&
 	test_cmp expect-other-name actual-other-name
 '
 
-# We are on 'master', which integrates with 'foo' from parent1
+# We are on 'main', which integrates with 'foo' from parent1
 # remote (set in test_pushdefault_workflow helper).  Push to
 # parent1 in centralized, and push to parent2 in triangular workflow.
-# The parent1 repository has 'master' and 'foo' branches, while
-# the parent2 repository has only 'master' branch.
+# The parent1 repository has 'main' and 'foo' branches, while
+# the parent2 repository has only 'main' branch.
 #
 # test_pushdefault_workflow() arguments:
 # $1 = success or failure
 # $2 = push.default value
-# $3 = branch to check for actual output (master or foo)
+# $3 = branch to check for actual output (main or foo)
 # $4 = [optional] switch to triangular workflow
 
-# update parent1's master (which is not our upstream)
-test_pushdefault_workflow success current master
+# update parent1's main (which is not our upstream)
+test_pushdefault_workflow success current main
 
 # update parent1's foo (which is our upstream)
 test_pushdefault_workflow success upstream foo
 
 # upstream is foo which is not the name of the current branch
-test_pushdefault_workflow failure simple master
+test_pushdefault_workflow failure simple main
 
-# master and foo are updated
-test_pushdefault_workflow success matching master
+# main and foo are updated
+test_pushdefault_workflow success matching main
 
-# master is updated
-test_pushdefault_workflow success current master triangular
+# main is updated
+test_pushdefault_workflow success current main triangular
 
 # upstream mode cannot be used in triangular
 test_pushdefault_workflow failure upstream foo triangular
 
 # in triangular, 'simple' works as 'current' and update the branch
 # with the same name.
-test_pushdefault_workflow success simple master triangular
+test_pushdefault_workflow success simple main triangular
 
-# master is updated (parent2 does not have foo)
-test_pushdefault_workflow success matching master triangular
+# main is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching main triangular
 
 # default tests, when no push-default is specified. This
 # should behave the same as "simple" in non-triangular
 # settings, and as "current" otherwise.
 
 test_expect_success 'default behavior allows "simple" push' '
-	test_config branch.master.remote parent1 &&
-	test_config branch.master.merge refs/heads/master &&
+	test_config branch.main.remote parent1 &&
+	test_config branch.main.merge refs/heads/main &&
 	test_config remote.pushdefault parent1 &&
-	test_commit default-master-master &&
-	test_push_success "" master
+	test_commit default-main-main &&
+	test_push_success "" main
 '
 
 test_expect_success 'default behavior rejects non-simple push' '
-	test_config branch.master.remote parent1 &&
-	test_config branch.master.merge refs/heads/foo &&
+	test_config branch.main.remote parent1 &&
+	test_config branch.main.merge refs/heads/foo &&
 	test_config remote.pushdefault parent1 &&
-	test_commit default-master-foo &&
+	test_commit default-main-foo &&
 	test_push_failure ""
 '
 
 test_expect_success 'default triangular behavior acts like "current"' '
-	test_config branch.master.remote parent1 &&
-	test_config branch.master.merge refs/heads/foo &&
+	test_config branch.main.remote parent1 &&
+	test_config branch.main.merge refs/heads/foo &&
 	test_config remote.pushdefault parent2 &&
 	test_commit default-triangular &&
-	test_push_success "" master repo2
+	test_push_success "" main repo2
 '
 
 test_done
diff --git a/t/t5529-push-errors.sh b/t/t5529-push-errors.sh
index 9871307..ce85fd3 100755
--- a/t/t5529-push-errors.sh
+++ b/t/t5529-push-errors.sh
@@ -32,7 +32,7 @@
 test_expect_success 'detect missing sha1 expressions early' '
 	echo no >rp-ran &&
 	echo no >expect &&
-	test_must_fail git push origin master~2:master &&
+	test_must_fail git push origin main~2:main &&
 	test_cmp expect rp-ran
 '
 
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index 9dd2d24..7c1460e 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -115,7 +115,7 @@
 
 test_expect_success 'fetch fails' '
 
-	test_must_fail git fetch .. master
+	test_must_fail git fetch .. main
 
 '
 
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
index 4ad059e..d573ca4 100755
--- a/t/t5531-deep-submodule-push.sh
+++ b/t/t5531-deep-submodule-push.sh
@@ -2,6 +2,9 @@
 
 test_description='test push with submodules'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -30,7 +33,7 @@
 test_expect_success 'push works with recorded gitlink' '
 	(
 		cd work &&
-		git push ../pub.git master
+		git push ../pub.git main
 	)
 '
 
@@ -45,7 +48,7 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Second commit for gar/bage" &&
-		git push --recurse-submodules=check ../pub.git master
+		git push --recurse-submodules=check ../pub.git main
 	)
 '
 
@@ -66,21 +69,21 @@
 		git commit -m "Third commit for gar/bage" &&
 		# the push should fail with --recurse-submodules=check
 		# on the command line...
-		test_must_fail git push --recurse-submodules=check ../pub.git master &&
+		test_must_fail git push --recurse-submodules=check ../pub.git main &&
 
 		# ...or if specified in the configuration..
-		test_must_fail git -c push.recurseSubmodules=check push ../pub.git master
+		test_must_fail git -c push.recurseSubmodules=check push ../pub.git main
 	)
 '
 
 test_expect_success 'push succeeds after commit was pushed to remote' '
 	(
 		cd work/gar/bage &&
-		git push origin master
+		git push origin main
 	) &&
 	(
 		cd work &&
-		git push --recurse-submodules=check ../pub.git master
+		git push --recurse-submodules=check ../pub.git main
 	)
 '
 
@@ -95,13 +98,13 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse on-demand on command line for gar/bage" &&
-		git push --recurse-submodules=on-demand ../pub.git master &&
+		git push --recurse-submodules=on-demand ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# Check that the submodule commit got there too
 		cd gar/bage &&
-		git diff --quiet origin/master master
+		git diff --quiet origin/main main
 	)
 '
 
@@ -116,13 +119,13 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse on-demand from config for gar/bage" &&
-		git -c push.recurseSubmodules=on-demand push ../pub.git master &&
+		git -c push.recurseSubmodules=on-demand push ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# Check that the submodule commit got there too
 		cd gar/bage &&
-		git diff --quiet origin/master master
+		git diff --quiet origin/main main
 	)
 '
 
@@ -137,13 +140,13 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse submodule.recurse from config for gar/bage" &&
-		git -c submodule.recurse push ../pub.git master &&
+		git -c submodule.recurse push ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# Check that the submodule commit got there too
 		cd gar/bage &&
-		git diff --quiet origin/master master
+		git diff --quiet origin/main main
 	)
 '
 
@@ -161,34 +164,34 @@
 
 		# Ensure that we can override on-demand in the config
 		# to just check submodules
-		test_must_fail git -c push.recurseSubmodules=on-demand push --recurse-submodules=check ../pub.git master &&
+		test_must_fail git -c push.recurseSubmodules=on-demand push --recurse-submodules=check ../pub.git main &&
 		# Check that the supermodule commit did not get there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master^ &&
+		git diff --quiet FETCH_HEAD main^ &&
 		# Check that the submodule commit did not get there
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# Ensure that we can override check in the config to
 		# disable submodule recursion entirely
-		(cd gar/bage && git diff --quiet origin/master master^) &&
-		git -c push.recurseSubmodules=on-demand push --recurse-submodules=no ../pub.git master &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
+		git -c push.recurseSubmodules=on-demand push --recurse-submodules=no ../pub.git main &&
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		git diff --quiet FETCH_HEAD main &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# Ensure that we can override check in the config to
 		# disable submodule recursion entirely (alternative form)
-		git -c push.recurseSubmodules=on-demand push --no-recurse-submodules ../pub.git master &&
+		git -c push.recurseSubmodules=on-demand push --no-recurse-submodules ../pub.git main &&
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		git diff --quiet FETCH_HEAD main &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# Ensure that we can override check in the config to
 		# push the submodule too
-		git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git master &&
+		git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git main &&
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
-		(cd gar/bage && git diff --quiet origin/master master)
+		git diff --quiet FETCH_HEAD main &&
+		(cd gar/bage && git diff --quiet origin/main main)
 	)
 '
 
@@ -205,31 +208,31 @@
 		git commit -m "Recurse on command-line overriding earlier command-line for gar/bage" &&
 
 		# should result in "check"
-		test_must_fail git push --recurse-submodules=on-demand --recurse-submodules=check ../pub.git master &&
+		test_must_fail git push --recurse-submodules=on-demand --recurse-submodules=check ../pub.git main &&
 		# Check that the supermodule commit did not get there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master^ &&
+		git diff --quiet FETCH_HEAD main^ &&
 		# Check that the submodule commit did not get there
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# should result in "no"
-		git push --recurse-submodules=on-demand --recurse-submodules=no ../pub.git master &&
+		git push --recurse-submodules=on-demand --recurse-submodules=no ../pub.git main &&
 		# Check that the supermodule commit did get there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# Check that the submodule commit did not get there
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# should result in "no"
-		git push --recurse-submodules=on-demand --no-recurse-submodules ../pub.git master &&
+		git push --recurse-submodules=on-demand --no-recurse-submodules ../pub.git main &&
 		# Check that the submodule commit did not get there
-		(cd gar/bage && git diff --quiet origin/master master^) &&
+		(cd gar/bage && git diff --quiet origin/main main^) &&
 
 		# But the options in the other order should push the submodule
-		git push --recurse-submodules=check --recurse-submodules=on-demand ../pub.git master &&
+		git push --recurse-submodules=check --recurse-submodules=on-demand ../pub.git main &&
 		# Check that the submodule commit did get there
 		git fetch ../pub.git &&
-		(cd gar/bage && git diff --quiet origin/master master)
+		(cd gar/bage && git diff --quiet origin/main main)
 	)
 '
 
@@ -244,13 +247,13 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse on-demand on command-line overriding config for gar/bage" &&
-		git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git master &&
+		git -c push.recurseSubmodules=check push --recurse-submodules=on-demand ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# Check that the submodule commit got there
 		cd gar/bage &&
-		git diff --quiet origin/master master
+		git diff --quiet origin/main main
 	)
 '
 
@@ -265,14 +268,14 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse disable on command-line overriding config for gar/bage" &&
-		git -c push.recurseSubmodules=check push --recurse-submodules=no ../pub.git master &&
+		git -c push.recurseSubmodules=check push --recurse-submodules=no ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# But that the submodule commit did not
-		( cd gar/bage && git diff --quiet origin/master master^ ) &&
+		( cd gar/bage && git diff --quiet origin/main main^ ) &&
 		# Now push it to avoid confusing future tests
-		git push --recurse-submodules=on-demand ../pub.git master
+		git push --recurse-submodules=on-demand ../pub.git main
 	)
 '
 
@@ -287,14 +290,14 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse disable on command-line alternative overriding config for gar/bage" &&
-		git -c push.recurseSubmodules=check push --no-recurse-submodules ../pub.git master &&
+		git -c push.recurseSubmodules=check push --no-recurse-submodules ../pub.git main &&
 		# Check that the supermodule commit got there
 		git fetch ../pub.git &&
-		git diff --quiet FETCH_HEAD master &&
+		git diff --quiet FETCH_HEAD main &&
 		# But that the submodule commit did not
-		( cd gar/bage && git diff --quiet origin/master master^ ) &&
+		( cd gar/bage && git diff --quiet origin/main main^ ) &&
 		# Now push it to avoid confusing future tests
-		git push --recurse-submodules=on-demand ../pub.git master
+		git push --recurse-submodules=on-demand ../pub.git main
 	)
 '
 
@@ -304,7 +307,7 @@
 	git -C work update-index --cacheinfo 160000 "$tag" gar/bage &&
 	git -C work commit -m "bad commit" &&
 	test_when_finished "git -C work reset --hard HEAD^" &&
-	test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git master 2>err &&
+	test_must_fail git -C work push --recurse-submodules=on-demand ../pub.git main 2>err &&
 	test_i18ngrep "is a tag, not a commit" err
 '
 
@@ -319,9 +322,9 @@
 		cd work &&
 		git add gar/bage &&
 		git commit -m "Recurse push fails if recurse submodules option passed as yes for gar/bage" &&
-		test_must_fail git push --recurse-submodules=yes ../pub.git master &&
-		test_must_fail git -c push.recurseSubmodules=yes push ../pub.git master &&
-		git push --recurse-submodules=on-demand ../pub.git master
+		test_must_fail git push --recurse-submodules=yes ../pub.git main &&
+		test_must_fail git -c push.recurseSubmodules=yes push ../pub.git main &&
+		git push --recurse-submodules=on-demand ../pub.git main
 	)
 '
 
@@ -363,7 +366,7 @@
 		) &&
 		git add b &&
 		git commit -m "added submodule" &&
-		git push --recurse-submodules=check origin master
+		git push --recurse-submodules=check origin main
 	)
 '
 
@@ -372,21 +375,21 @@
 		cd work &&
 		(
 			cd gar/bage &&
-			git checkout master &&
+			git checkout main &&
 			>junk5 &&
 			git add junk5 &&
 			git commit -m "Fifth junk" &&
 			git push &&
-			git rev-parse origin/master >../../../expected
+			git rev-parse origin/main >../../../expected
 		) &&
-		git checkout master &&
+		git checkout main &&
 		git add gar/bage &&
 		git commit -m "Fifth commit for gar/bage" &&
-		git push --recurse-submodules=on-demand ../pub.git master
+		git push --recurse-submodules=on-demand ../pub.git main
 	) &&
 	(
 		cd submodule.git &&
-		git rev-parse master >../actual
+		git rev-parse main >../actual
 	) &&
 	test_cmp expected actual
 '
@@ -394,7 +397,7 @@
 test_expect_success 'push unpushed submodules when not needed 2' '
 	(
 		cd submodule.git &&
-		git rev-parse master >../expected
+		git rev-parse main >../expected
 	) &&
 	(
 		cd work &&
@@ -407,11 +410,11 @@
 		>junk2 &&
 		git add junk2 &&
 		git commit -m "Second junk for work" &&
-		git push --recurse-submodules=on-demand ../pub.git master
+		git push --recurse-submodules=on-demand ../pub.git main
 	) &&
 	(
 		cd submodule.git &&
-		git rev-parse master >../actual
+		git rev-parse main >../actual
 	) &&
 	test_cmp expected actual
 '
@@ -421,20 +424,20 @@
 		cd work &&
 		(
 			cd gar/bage &&
-			git checkout master &&
+			git checkout main &&
 			> junk7 &&
 			git add junk7 &&
 			git commit -m "Seventh junk" &&
-			git rev-parse master >../../../expected
+			git rev-parse main >../../../expected
 		) &&
-		git checkout master &&
+		git checkout main &&
 		git add gar/bage &&
 		git commit -m "Seventh commit for gar/bage" &&
-		git push --recurse-submodules=on-demand ../pub.git master
+		git push --recurse-submodules=on-demand ../pub.git main
 	) &&
 	(
 		cd submodule.git &&
-		git rev-parse master >../actual
+		git rev-parse main >../actual
 	) &&
 	test_cmp expected actual
 '
@@ -444,64 +447,64 @@
 		cd work &&
 		(
 			cd gar/bage &&
-			git rev-parse origin/master >../../../expected &&
-			git checkout master~0 &&
+			git rev-parse origin/main >../../../expected &&
+			git checkout main~0 &&
 			> junk8 &&
 			git add junk8 &&
 			git commit -m "Eighth junk"
 		) &&
 		git add gar/bage &&
 		git commit -m "Eighth commit for gar/bage" &&
-		test_must_fail git push --recurse-submodules=on-demand ../pub.git master
+		test_must_fail git push --recurse-submodules=on-demand ../pub.git main
 	) &&
 	(
 		cd submodule.git &&
-		git rev-parse master >../actual
+		git rev-parse main >../actual
 	) &&
-	test_when_finished git -C work reset --hard master^ &&
+	test_when_finished git -C work reset --hard main^ &&
 	test_cmp expected actual
 '
 
 test_expect_success 'push --dry-run does not recursively update submodules' '
 	(
 		cd work/gar/bage &&
-		git checkout master &&
-		git rev-parse master >../../../expected_submodule &&
+		git checkout main &&
+		git rev-parse main >../../../expected_submodule &&
 		> junk9 &&
 		git add junk9 &&
 		git commit -m "Ninth junk" &&
 
 		# Go up to 'work' directory
 		cd ../.. &&
-		git checkout master &&
-		git rev-parse master >../expected_pub &&
+		git checkout main &&
+		git rev-parse main >../expected_pub &&
 		git add gar/bage &&
 		git commit -m "Ninth commit for gar/bage" &&
-		git push --dry-run --recurse-submodules=on-demand ../pub.git master
+		git push --dry-run --recurse-submodules=on-demand ../pub.git main
 	) &&
-	git -C submodule.git rev-parse master >actual_submodule &&
-	git -C pub.git rev-parse master >actual_pub &&
+	git -C submodule.git rev-parse main >actual_submodule &&
+	git -C pub.git rev-parse main >actual_pub &&
 	test_cmp expected_pub actual_pub &&
 	test_cmp expected_submodule actual_submodule
 '
 
 test_expect_success 'push --dry-run does not recursively update submodules' '
-	git -C work push --dry-run --recurse-submodules=only ../pub.git master &&
+	git -C work push --dry-run --recurse-submodules=only ../pub.git main &&
 
-	git -C submodule.git rev-parse master >actual_submodule &&
-	git -C pub.git rev-parse master >actual_pub &&
+	git -C submodule.git rev-parse main >actual_submodule &&
+	git -C pub.git rev-parse main >actual_pub &&
 	test_cmp expected_pub actual_pub &&
 	test_cmp expected_submodule actual_submodule
 '
 
 test_expect_success 'push only unpushed submodules recursively' '
-	git -C work/gar/bage rev-parse master >expected_submodule &&
-	git -C pub.git rev-parse master >expected_pub &&
+	git -C work/gar/bage rev-parse main >expected_submodule &&
+	git -C pub.git rev-parse main >expected_pub &&
 
-	git -C work push --recurse-submodules=only ../pub.git master &&
+	git -C work push --recurse-submodules=only ../pub.git main &&
 
-	git -C submodule.git rev-parse master >actual_submodule &&
-	git -C pub.git rev-parse master >actual_pub &&
+	git -C submodule.git rev-parse main >actual_submodule &&
+	git -C pub.git rev-parse main >actual_pub &&
 	test_cmp expected_submodule actual_submodule &&
 	test_cmp expected_pub actual_pub
 '
@@ -517,14 +520,14 @@
 	git -C work commit -m "Tenth junk added to gar/bage" &&
 
 	# Fails when submodule does not have a matching remote
-	test_must_fail git -C work push --recurse-submodules=on-demand pub master &&
+	test_must_fail git -C work push --recurse-submodules=on-demand pub main &&
 	# Succeeds when submodules has matching remote and refspec
-	git -C work push --recurse-submodules=on-demand origin master &&
+	git -C work push --recurse-submodules=on-demand origin main &&
 
-	git -C submodule.git rev-parse master >actual_submodule &&
-	git -C pub.git rev-parse master >actual_pub &&
-	git -C work/gar/bage rev-parse master >expected_submodule &&
-	git -C work rev-parse master >expected_pub &&
+	git -C submodule.git rev-parse main >actual_submodule &&
+	git -C pub.git rev-parse main >actual_pub &&
+	git -C work/gar/bage rev-parse main >expected_submodule &&
+	git -C work rev-parse main >expected_pub &&
 	test_cmp expected_submodule actual_submodule &&
 	test_cmp expected_pub actual_pub
 '
@@ -548,7 +551,7 @@
 	test_must_fail git -C work push --recurse-submodules=on-demand origin \
 		HEAD:refs/heads/branch2 &&
 
-	git -C work/gar/bage branch branch2 master &&
+	git -C work/gar/bage branch branch2 main &&
 	git -C work push --recurse-submodules=on-demand origin branch2 &&
 
 	git -C submodule.git rev-parse branch2 >actual_submodule &&
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
index 7813e84..cba26a8 100755
--- a/t/t5533-push-cas.sh
+++ b/t/t5533-push-cas.sh
@@ -2,6 +2,9 @@
 
 test_description='compare & swap push force/delete safety'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 setup_srcdst_basic () {
@@ -29,22 +32,22 @@
 	(
 		cd dup &&
 		git fetch &&
-		git merge origin/master &&
-		git switch -c branch master~2 &&
+		git merge origin/main &&
+		git switch -c branch main~2 &&
 		test_commit D &&
 		test_commit E &&
 		git push origin --all
 	) &&
 	(
 		cd src &&
-		git switch master &&
+		git switch main &&
 		git fetch --all &&
 		git branch branch --track origin/branch &&
-		git rebase origin/master
+		git rebase origin/main
 	) &&
 	(
 		cd dup &&
-		git switch master &&
+		git switch main &&
 		test_commit F &&
 		test_commit G &&
 		git switch branch &&
@@ -65,11 +68,11 @@
 	(
 		cd dst &&
 		test_commit D &&
-		test_must_fail git push --force-with-lease=master:master origin master 2>err &&
+		test_must_fail git push --force-with-lease=main:main origin main 2>err &&
 		grep "stale info" err
 	) &&
-	git ls-remote . refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote . refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -78,11 +81,11 @@
 	(
 		cd dst &&
 		test_commit D &&
-		git push --force --force-with-lease=master:master origin master 2>err &&
+		git push --force --force-with-lease=main:main origin main 2>err &&
 		grep "forced update" err
 	) &&
-	git ls-remote dst refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote dst refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -90,20 +93,20 @@
 	setup_srcdst_basic &&
 	(
 		cd src &&
-		git checkout master &&
+		git checkout main &&
 		test_commit D &&
 		git checkout HEAD^0
 	) &&
-	git ls-remote src refs/heads/master >expect &&
+	git ls-remote src refs/heads/main >expect &&
 	(
 		cd dst &&
 		test_commit E &&
-		git ls-remote . refs/remotes/origin/master >expect &&
-		test_must_fail git push --force-with-lease=master origin master &&
-		git ls-remote . refs/remotes/origin/master >actual &&
+		git ls-remote . refs/remotes/origin/main >expect &&
+		test_must_fail git push --force-with-lease=main origin main &&
+		git ls-remote . refs/remotes/origin/main >actual &&
 		test_cmp expect actual
 	) &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -111,18 +114,18 @@
 	setup_srcdst_basic &&
 	(
 		cd src &&
-		git checkout master &&
+		git checkout main &&
 		test_commit D &&
 		git checkout HEAD^0
 	) &&
 	(
 		cd dst &&
 		test_commit E &&
-		git ls-remote . refs/remotes/origin/master >expect &&
-		git push --force --force-with-lease=master origin master
+		git ls-remote . refs/remotes/origin/main >expect &&
+		git push --force --force-with-lease=main origin main
 	) &&
-	git ls-remote dst refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote dst refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -131,10 +134,10 @@
 	(
 		cd dst &&
 		test_commit D &&
-		git push --force-with-lease=master:master^ origin master
+		git push --force-with-lease=main:main^ origin main
 	) &&
-	git ls-remote dst refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote dst refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -143,11 +146,11 @@
 	(
 		cd dst &&
 		test_commit D &&
-		git push --force-with-lease=master origin master 2>err &&
+		git push --force-with-lease=main origin main 2>err &&
 		! grep "forced update" err
 	) &&
-	git ls-remote dst refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote dst refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -157,22 +160,22 @@
 		cd dst &&
 		git reset --hard HEAD^ &&
 		test_commit D &&
-		git push --force-with-lease=master origin master 2>err &&
+		git push --force-with-lease=main origin main 2>err &&
 		grep "forced update" err
 	) &&
-	git ls-remote dst refs/heads/master >expect &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote dst refs/heads/main >expect &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'push to delete (protected)' '
 	setup_srcdst_basic &&
-	git ls-remote src refs/heads/master >expect &&
+	git ls-remote src refs/heads/main >expect &&
 	(
 		cd dst &&
-		test_must_fail git push --force-with-lease=master:master^ origin :master
+		test_must_fail git push --force-with-lease=main:main^ origin :main
 	) &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote src refs/heads/main >actual &&
 	test_cmp expect actual
 '
 
@@ -180,9 +183,9 @@
 	setup_srcdst_basic &&
 	(
 		cd dst &&
-		git push --force --force-with-lease=master:master^ origin :master
+		git push --force --force-with-lease=main:main^ origin :main
 	) &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote src refs/heads/main >actual &&
 	test_must_be_empty actual
 '
 
@@ -190,10 +193,10 @@
 	setup_srcdst_basic &&
 	(
 		cd dst &&
-		git push --force-with-lease=master origin :master 2>err &&
+		git push --force-with-lease=main origin :main 2>err &&
 		grep deleted err
 	) &&
-	git ls-remote src refs/heads/master >actual &&
+	git ls-remote src refs/heads/main >actual &&
 	test_must_be_empty actual
 '
 
@@ -201,12 +204,12 @@
 	setup_srcdst_basic &&
 	(
 		cd src &&
-		git branch naster master^
+		git branch nain main^
 	) &&
 	git ls-remote src refs/heads/\* >expect &&
 	(
 		cd dst &&
-		test_must_fail git push --force-with-lease origin master master:naster
+		test_must_fail git push --force-with-lease origin main main:nain
 	) &&
 	git ls-remote src refs/heads/\* >actual &&
 	test_cmp expect actual
@@ -216,16 +219,16 @@
 	setup_srcdst_basic &&
 	(
 		cd src &&
-		git branch naster master^
+		git branch nain main^
 	) &&
 	(
 		cd dst &&
 		git fetch &&
-		git push --force-with-lease origin master master:naster
+		git push --force-with-lease origin main main:nain
 	) &&
-	git ls-remote dst refs/heads/master |
-	sed -e "s/master/naster/" >expect &&
-	git ls-remote src refs/heads/naster >actual &&
+	git ls-remote dst refs/heads/main |
+	sed -e "s/main/nain/" >expect &&
+	git ls-remote src refs/heads/nain >actual &&
 	test_cmp expect actual
 '
 
@@ -233,7 +236,7 @@
 	setup_srcdst_basic &&
 	(
 		cd dst &&
-		git branch branch master &&
+		git branch branch main &&
 		git push --force-with-lease=branch origin branch
 	) &&
 	git ls-remote dst refs/heads/branch >expect &&
@@ -245,7 +248,7 @@
 	setup_srcdst_basic &&
 	(
 		cd dst &&
-		git branch branch master &&
+		git branch branch main &&
 		git push --force-with-lease=branch: origin branch
 	) &&
 	git ls-remote dst refs/heads/branch >expect &&
@@ -257,12 +260,12 @@
 	setup_srcdst_basic &&
 	(
 		cd src &&
-		git checkout -b branch master &&
+		git checkout -b branch main &&
 		test_commit F
 	) &&
 	(
 		cd dst &&
-		git branch branch master &&
+		git branch branch main &&
 		test_must_fail git push --force-with-lease=branch: origin branch
 	)
 '
@@ -277,7 +280,7 @@
 		cd dst &&
 		test_commit G &&
 		git remote add origin-push ../src.bare &&
-		git push origin-push master:master
+		git push origin-push main:main
 	) &&
 	git clone --no-local src.bare dst2 &&
 	test_when_finished "rm -rf dst2" &&
@@ -299,58 +302,58 @@
 test_expect_success 'background updates to remote can be mitigated with "--force-if-includes"' '
 	setup_src_dup_dst &&
 	test_when_finished "rm -fr dst src dup" &&
-	git ls-remote dst refs/heads/master >expect.master &&
+	git ls-remote dst refs/heads/main >expect.main &&
 	git ls-remote dst refs/heads/branch >expect.branch &&
 	(
 		cd src &&
 		git switch branch &&
 		test_commit I &&
-		git switch master &&
+		git switch main &&
 		test_commit J &&
 		git fetch --all &&
 		test_must_fail git push --force-with-lease --force-if-includes --all
 	) &&
-	git ls-remote dst refs/heads/master >actual.master &&
+	git ls-remote dst refs/heads/main >actual.main &&
 	git ls-remote dst refs/heads/branch >actual.branch &&
-	test_cmp expect.master actual.master &&
+	test_cmp expect.main actual.main &&
 	test_cmp expect.branch actual.branch
 '
 
 test_expect_success 'background updates to remote can be mitigated with "push.useForceIfIncludes"' '
 	setup_src_dup_dst &&
 	test_when_finished "rm -fr dst src dup" &&
-	git ls-remote dst refs/heads/master >expect.master &&
+	git ls-remote dst refs/heads/main >expect.main &&
 	(
 		cd src &&
 		git switch branch &&
 		test_commit I &&
-		git switch master &&
+		git switch main &&
 		test_commit J &&
 		git fetch --all &&
 		git config --local push.useForceIfIncludes true &&
-		test_must_fail git push --force-with-lease=master origin master
+		test_must_fail git push --force-with-lease=main origin main
 	) &&
-	git ls-remote dst refs/heads/master >actual.master &&
-	test_cmp expect.master actual.master
+	git ls-remote dst refs/heads/main >actual.main &&
+	test_cmp expect.main actual.main
 '
 
 test_expect_success '"--force-if-includes" should be disabled for --force-with-lease="<refname>:<expect>"' '
 	setup_src_dup_dst &&
 	test_when_finished "rm -fr dst src dup" &&
-	git ls-remote dst refs/heads/master >expect.master &&
+	git ls-remote dst refs/heads/main >expect.main &&
 	(
 		cd src &&
 		git switch branch &&
 		test_commit I &&
-		git switch master &&
+		git switch main &&
 		test_commit J &&
-		remote_head="$(git rev-parse refs/remotes/origin/master)" &&
+		remote_head="$(git rev-parse refs/remotes/origin/main)" &&
 		git fetch --all &&
-		test_must_fail git push --force-if-includes --force-with-lease="master:$remote_head" 2>err &&
+		test_must_fail git push --force-if-includes --force-with-lease="main:$remote_head" 2>err &&
 		grep "stale info" err
 	) &&
-	git ls-remote dst refs/heads/master >actual.master &&
-	test_cmp expect.master actual.master
+	git ls-remote dst refs/heads/main >actual.main &&
+	test_cmp expect.main actual.main
 '
 
 test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase")' '
@@ -360,10 +363,10 @@
 		cd src &&
 		git switch branch &&
 		test_commit I &&
-		git switch master &&
+		git switch main &&
 		test_commit J &&
-		git pull --rebase origin master &&
-		git push --force-if-includes --force-with-lease="master"
+		git pull --rebase origin main &&
+		git push --force-if-includes --force-with-lease="main"
 	)
 '
 
@@ -374,11 +377,11 @@
 		cd src &&
 		git switch branch &&
 		test_commit I &&
-		git switch master &&
+		git switch main &&
 		test_commit J &&
-		git pull --rebase origin master &&
+		git pull --rebase origin main &&
 		git rebase --onto HEAD~4 HEAD~1 &&
-		git push --force-if-includes --force-with-lease="master"
+		git push --force-if-includes --force-with-lease="main"
 	)
 '
 
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index af0385f..bba768f 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -2,6 +2,9 @@
 
 test_description='signed push'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
@@ -9,11 +12,11 @@
 	rm -fr dst &&
 	test_create_repo dst &&
 
-	git push dst master:noop master:ff master:noff
+	git push dst main:noop main:ff main:noff
 }
 
 test_expect_success setup '
-	# master, ff and noff branches pointing at the same commit
+	# main, ff and noff branches pointing at the same commit
 	test_tick &&
 	git commit --allow-empty -m initial &&
 
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index a55202d..11d5ea5 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -2,6 +2,9 @@
 
 test_description='fetch/clone from a shallow clone'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 commit() {
@@ -49,7 +52,7 @@
 	cd shallow2 &&
 	git fetch &&
 	git fsck &&
-	git log --format=%s origin/master >actual &&
+	git log --format=%s origin/main >actual &&
 	test_write_lines 5 4 3 >expect &&
 	test_cmp expect actual
 	)
@@ -64,7 +67,7 @@
 	cd shallow2 &&
 	git fetch --depth=2 &&
 	git fsck &&
-	git log --format=%s origin/master >actual &&
+	git log --format=%s origin/main >actual &&
 	test_write_lines 6 5 >expect &&
 	test_cmp expect actual
 	)
@@ -75,7 +78,7 @@
 	cd shallow2 &&
 	git fetch --unshallow &&
 	git fsck &&
-	git log --format=%s origin/master >actual &&
+	git log --format=%s origin/main >actual &&
 	test_write_lines 6 5 4 3 >expect &&
 	test_cmp expect actual
 	)
@@ -89,7 +92,7 @@
 	test_write_lines 4 3 >expect &&
 	test_cmp expect actual &&
 	git -c fetch.writeCommitGraph fetch --unshallow &&
-	git log origin/master --format=%s >actual &&
+	git log origin/main --format=%s >actual &&
 	test_write_lines 4 3 2 1 >expect &&
 	test_cmp expect actual
 	)
@@ -104,9 +107,9 @@
 	git commit -m add-1-back &&
 	(
 	cd shallow2 &&
-	git fetch ../.git +refs/heads/master:refs/remotes/top/master &&
+	git fetch ../.git +refs/heads/main:refs/remotes/top/main &&
 	git fsck &&
-	git log --format=%s top/master >actual &&
+	git log --format=%s top/main >actual &&
 	test_write_lines add-1-back 4 3 >expect &&
 	test_cmp expect actual
 	) &&
@@ -135,7 +138,7 @@
 test_expect_success 'fetch --update-shallow' '
 	(
 	cd shallow &&
-	git checkout master &&
+	git checkout main &&
 	commit 7 &&
 	git tag -m foo heavy-tag HEAD^ &&
 	git tag light-tag HEAD^:tracked
@@ -146,13 +149,13 @@
 	git fsck &&
 	git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
 	cat <<-\EOF >expect.refs &&
-	refs/remotes/shallow/master
+	refs/remotes/shallow/main
 	refs/remotes/shallow/no-shallow
 	refs/tags/heavy-tag
 	refs/tags/light-tag
 	EOF
 	test_cmp expect.refs actual.refs &&
-	git log --format=%s shallow/master >actual &&
+	git log --format=%s shallow/main >actual &&
 	test_write_lines 7 6 5 4 3 >expect &&
 	test_cmp expect actual
 	)
@@ -161,7 +164,7 @@
 test_expect_success 'fetch --update-shallow (with fetch.writeCommitGraph)' '
 	(
 	cd shallow &&
-	git checkout master &&
+	git checkout main &&
 	commit 8 &&
 	git tag -m foo heavy-tag-for-graph HEAD^ &&
 	git tag light-tag-for-graph HEAD^:tracked
@@ -173,7 +176,7 @@
 	git fsck &&
 	git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
 	cat <<-EOF >expect.refs &&
-	refs/remotes/shallow/master
+	refs/remotes/shallow/main
 	refs/remotes/shallow/no-shallow
 	refs/tags/heavy-tag
 	refs/tags/heavy-tag-for-graph
@@ -181,7 +184,7 @@
 	refs/tags/light-tag-for-graph
 	EOF
 	test_cmp expect.refs actual.refs &&
-	git log --format=%s shallow/master >actual &&
+	git log --format=%s shallow/main >actual &&
 	test_write_lines 8 7 6 5 4 3 >expect &&
 	test_cmp expect actual
 	)
@@ -206,7 +209,7 @@
 	test_commit -C shallow-server E &&
 	test_commit -C shallow-server D &&
 	d="$(git -C shallow-server rev-parse --verify D^0)" &&
-	git -C shallow-server checkout master &&
+	git -C shallow-server checkout main &&
 
 	git clone --depth=1 --no-tags --no-single-branch \
 		"file://$PWD/shallow-server" shallow-client &&
@@ -244,7 +247,7 @@
 	git -C "$REPO" config protocol.version 2 &&
 	git -C client config protocol.version 2 &&
 
-	git -C client fetch --depth=2 "$HTTPD_URL/one_time_perl/repo" master:a_branch &&
+	git -C client fetch --depth=2 "$HTTPD_URL/one_time_perl/repo" main:a_branch &&
 
 	# Craft a situation in which the server sends back an unshallow request
 	# with an empty packfile. This is done by refetching with a shorter
@@ -256,7 +259,7 @@
 	       >"$HTTPD_ROOT_PATH/one-time-perl" &&
 	test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
 		fetch --depth=1 "$HTTPD_URL/one_time_perl/repo" \
-		master:a_branch &&
+		main:a_branch &&
 
 	# Ensure that the one-time-perl script was used.
 	! test -e "$HTTPD_ROOT_PATH/one-time-perl" &&
diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh
index ecbf84d..e91fcc1 100755
--- a/t/t5538-push-shallow.sh
+++ b/t/t5538-push-shallow.sh
@@ -2,6 +2,9 @@
 
 test_description='push from/to a shallow clone'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 commit() {
@@ -44,9 +47,9 @@
 	(
 	cd shallow &&
 	commit 5 &&
-	git push ../.git +master:refs/remotes/shallow/master
+	git push ../.git +main:refs/remotes/shallow/main
 	) &&
-	git log --format=%s shallow/master >actual &&
+	git log --format=%s shallow/main >actual &&
 	git fsck &&
 	cat <<EOF >expect &&
 5
@@ -61,10 +64,10 @@
 test_expect_success 'push from shallow clone, with grafted roots' '
 	(
 	cd shallow2 &&
-	test_must_fail git push ../.git +master:refs/remotes/shallow2/master 2>err &&
-	grep "shallow2/master.*shallow update not allowed" err
+	test_must_fail git push ../.git +main:refs/remotes/shallow2/main 2>err &&
+	grep "shallow2/main.*shallow update not allowed" err
 	) &&
-	test_must_fail git rev-parse shallow2/master &&
+	test_must_fail git rev-parse shallow2/main &&
 	git fsck
 '
 
@@ -72,9 +75,9 @@
 	test_config receive.shallowupdate true &&
 	(
 	cd shallow2 &&
-	git push ../.git +master:refs/remotes/shallow2/master
+	git push ../.git +main:refs/remotes/shallow2/main
 	) &&
-	git log --format=%s shallow2/master >actual &&
+	git log --format=%s shallow2/main >actual &&
 	git fsck &&
 	cat <<EOF >expect &&
 c
@@ -87,12 +90,12 @@
 	(
 	cd shallow &&
 	git --git-dir=../shallow2/.git config receive.shallowupdate true &&
-	git push ../shallow2/.git +master:refs/remotes/shallow/master &&
+	git push ../shallow2/.git +main:refs/remotes/shallow/main &&
 	git --git-dir=../shallow2/.git config receive.shallowupdate false
 	) &&
 	(
 	cd shallow2 &&
-	git log --format=%s shallow/master >actual &&
+	git log --format=%s shallow/main >actual &&
 	git fsck &&
 	cat <<EOF >expect &&
 5
@@ -106,10 +109,10 @@
 test_expect_success 'push from full to shallow' '
 	! git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) &&
 	commit 1 &&
-	git push shallow2/.git +master:refs/remotes/top/master &&
+	git push shallow2/.git +main:refs/remotes/top/main &&
 	(
 	cd shallow2 &&
-	git log --format=%s top/master >actual &&
+	git log --format=%s top/main >actual &&
 	git fsck &&
 	cat <<EOF >expect &&
 1
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
index 82aa99a..3ea75d3 100755
--- a/t/t5539-fetch-http-shallow.sh
+++ b/t/t5539-fetch-http-shallow.sh
@@ -2,6 +2,9 @@
 
 test_description='fetch/clone from a shallow clone over http'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
@@ -32,7 +35,7 @@
 	(
 	cd clone &&
 	git fsck &&
-	git log --format=%s origin/master >actual &&
+	git log --format=%s origin/main >actual &&
 	cat <<EOF >expect &&
 7
 6
@@ -60,9 +63,9 @@
 				refs/heads/unrelated$i:refs/heads/unrelated$i ||
 			exit 1
 		done &&
-		git checkout master &&
+		git checkout main &&
 		test_commit new &&
-		git push  "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" master
+		git push  "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" main
 	) &&
 	(
 		cd clone &&
@@ -95,7 +98,7 @@
 
 test_expect_success 'fetch shallow since ...' '
 	git -C shallow11 fetch --shallow-since "200000000 +0700" origin &&
-	git -C shallow11 log --pretty=tformat:%s origin/master >actual &&
+	git -C shallow11 log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	three
 	two
@@ -120,7 +123,7 @@
 
 test_expect_success 'fetch exclude tag one' '
 	git -C shallow12 fetch --shallow-exclude one origin &&
-	git -C shallow12 log --pretty=tformat:%s origin/master >actual &&
+	git -C shallow12 log --pretty=tformat:%s origin/main >actual &&
 	test_write_lines three two >expected &&
 	test_cmp expected actual
 '
@@ -136,12 +139,12 @@
 	git clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen &&
 	mv "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" .git &&
 	test_commit four &&
-	git -C deepen log --pretty=tformat:%s master >actual &&
+	git -C deepen log --pretty=tformat:%s main >actual &&
 	echo three >expected &&
 	test_cmp expected actual &&
 	mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" &&
 	git -C deepen fetch --deepen=1 &&
-	git -C deepen log --pretty=tformat:%s origin/master >actual &&
+	git -C deepen log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	four
 	three
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
index 450321f..8b68bb3 100755
--- a/t/t5540-http-push-webdav.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -7,6 +7,9 @@
 
 This test runs various sanity checks on http-push.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
@@ -71,7 +74,7 @@
 test_expect_success 'push to remote repository with unpacked refs' '
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
 	 rm packed-refs &&
-	 git update-ref refs/heads/master $ORIG_HEAD &&
+	 git update-ref refs/heads/main $ORIG_HEAD &&
 	 git --bare update-server-info) &&
 	git push &&
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
@@ -91,7 +94,7 @@
 	 git remote rm origin &&
 	 git reflog expire --expire=0 --all &&
 	 git prune &&
-	 git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
+	 git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git main)
 '
 
 test_expect_success 'http-push fetches packed objects' '
@@ -111,7 +114,7 @@
 	 git remote remove origin &&
 	 git reflog expire --expire=0 --all &&
 	 git prune &&
-	 git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
+	 git push -f -v $HTTPD_URL/dumb/test_repo_packed.git main)
 '
 
 test_expect_success 'create and delete remote branch' '
@@ -163,7 +166,7 @@
 '
 
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-	"$ROOT_PATH"/test_repo_clone master
+	"$ROOT_PATH"/test_repo_clone main
 
 test_expect_success 'push to password-protected repository (user in URL)' '
 	test_commit pw-user &&
diff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh
index 187454f..c024fa2 100755
--- a/t/t5541-http-push-smart.sh
+++ b/t/t5541-http-push-smart.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='test smart pushing over http via http-backend'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 ROOT_PATH="$PWD"
@@ -115,7 +118,7 @@
 	git commit -m dev2 &&
 	test_must_fail git push origin dev2 2>act &&
 	sed -e "/^remote: /s/ *$//" <act >cmp &&
-	test_i18ncmp exp cmp
+	test_cmp exp cmp
 '
 rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
 
@@ -142,23 +145,23 @@
 '
 
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
-	"$ROOT_PATH"/test_repo_clone master 		success
+	"$ROOT_PATH"/test_repo_clone main 		success
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
 	# create a dissimilarly-named remote ref so that git is unable to match the
 	# two refs (viz. local, remote) unless an explicit refspec is provided.
-	git push origin master:retsam &&
+	git push origin main:niam &&
 
 	echo "change changed" > path2 &&
 	git commit -a -m path2 --amend &&
 
-	# push master too; this ensures there is at least one '"'push'"' command to
+	# push main too; this ensures there is at least one '"'push'"' command to
 	# the remote helper and triggers interaction with the helper.
-	test_must_fail git push -v origin +master master:retsam >output 2>&1'
+	test_must_fail git push -v origin +main main:niam >output 2>&1'
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: remote output' '
-	grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
-	grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output
+	grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *main -> main (forced update)$" output &&
+	grep "^ ! \[rejected\] *main -> niam (non-fast-forward)$" output
 '
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
@@ -167,7 +170,7 @@
 '
 
 test_expect_success 'push (chunked)' '
-	git checkout master &&
+	git checkout main &&
 	test_commit commit path3 &&
 	HEAD=$(git rev-parse --verify HEAD) &&
 	test_config http.postbuffer 4 &&
@@ -177,9 +180,9 @@
 	 test $HEAD = $(git rev-parse --verify HEAD))
 '
 
-## References of remote: atomic1(1)            master(2) collateral(2) other(2)
-## References of local :            atomic2(2) master(1) collateral(3) other(2) collateral1(3) atomic(1)
-## Atomic push         :                       master(1) collateral(3)                         atomic(1)
+## References of remote: atomic1(1)            main(2) collateral(2) other(2)
+## References of local :            atomic2(2) main(1) collateral(3) other(2) collateral1(3) atomic(1)
+## Atomic push         :                       main(1) collateral(3)                         atomic(1)
 test_expect_success 'push --atomic also prevents branch creation, reports collateral' '
 	# Setup upstream repo - empty for now
 	d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
@@ -192,15 +195,15 @@
 	test_commit atomic2 &&
 	git branch collateral &&
 	git branch other &&
-	git push "$up" atomic1 master collateral other &&
+	git push "$up" atomic1 main collateral other &&
 	git tag -d atomic1 &&
 
 	# collateral is a valid push, but should be failed by atomic push
 	git checkout collateral &&
 	test_commit collateral1 &&
 
-	# Make master incompatible with upstream to provoke atomic
-	git checkout master &&
+	# Make main incompatible with upstream to provoke atomic
+	git checkout main &&
 	git reset --hard HEAD^ &&
 
 	# Add a new branch which should be failed by atomic push. This is a
@@ -208,7 +211,7 @@
 	git branch atomic &&
 
 	# --atomic should cause entire push to be rejected
-	test_must_fail git push --atomic "$up" master atomic collateral 2>output &&
+	test_must_fail git push --atomic "$up" main atomic collateral 2>output &&
 
 	# the new branch should not have been created upstream
 	test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
@@ -216,15 +219,15 @@
 	# upstream should still reflect atomic2, the last thing we pushed
 	# successfully
 	git rev-parse atomic2 >expected &&
-	# on master...
-	git -C "$d" rev-parse refs/heads/master >actual &&
+	# on main...
+	git -C "$d" rev-parse refs/heads/main >actual &&
 	test_cmp expected actual &&
 	# ...and collateral.
 	git -C "$d" rev-parse refs/heads/collateral >actual &&
 	test_cmp expected actual &&
 
 	# the failed refs should be indicated to the user
-	grep "^ ! .*rejected.* master -> master" output &&
+	grep "^ ! .*rejected.* main -> main" output &&
 
 	# the collateral failure refs should be indicated to the user
 	grep "^ ! .*rejected.* atomic -> atomic .*atomic push failed" output &&
@@ -472,9 +475,9 @@
 	test_commit prepare-for-force-fetch &&
 	git switch -c away &&
 	git fetch "$HTTPD_URL_USER_PASS/smart/test_repo.git" \
-		+master:master &&
+		+main:main &&
 	# should have been scrubbed down to vanilla URL
-	git log -g master >reflog &&
+	git log -g main >reflog &&
 	grep "$HTTPD_URL" reflog &&
 	! grep "$HTTPD_URL_USER_PASS" reflog
 '
@@ -498,7 +501,7 @@
 	cd "$ROOT_PATH"/test_repo_clone &&
 	test_must_fail git -c color.transport=always -c color.advice=always \
 		-c color.push=always \
-		push origin origin/master^:master 2>act &&
+		push origin origin/main^:main 2>act &&
 	test_decode_color <act >decoded &&
 	test_i18ngrep "<RED>.*rejected.*<RESET>" decoded &&
 	test_i18ngrep "<RED>error: failed to push some refs" decoded &&
diff --git a/t/t5542-push-http-shallow.sh b/t/t5542-push-http-shallow.sh
index ddc1db7..c2cc831 100755
--- a/t/t5542-push-http-shallow.sh
+++ b/t/t5542-push-http-shallow.sh
@@ -2,6 +2,9 @@
 
 test_description='push from/to a shallow clone over http'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
@@ -51,12 +54,12 @@
 	(
 	cd full &&
 	commit 9 &&
-	git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master
+	git push $HTTPD_URL/smart/repo.git +main:refs/remotes/top/main
 	) &&
 	(
 	cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	git fsck &&
-	git log --format=%s top/master >actual &&
+	git log --format=%s top/main >actual &&
 	cat <<EOF >expect &&
 9
 4
@@ -74,11 +77,11 @@
 	git config http.receivepack true
 	) &&
 	commit 10 &&
-	git push $HTTPD_URL/smart/repo.git +master:refs/remotes/top/master &&
+	git push $HTTPD_URL/smart/repo.git +main:refs/remotes/top/main &&
 	(
 	cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	git fsck &&
-	git log --format=%s top/master >actual &&
+	git log --format=%s top/main >actual &&
 	cat <<EOF >expect &&
 10
 4
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 620c30d..bfee461 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -2,6 +2,9 @@
 
 test_description='pushing to a repository using the atomic push option'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 mk_repo_pair () {
@@ -40,9 +43,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git push --atomic up master
+		git push --atomic up main
 	) &&
-	test_refs master master
+	test_refs main main
 '
 
 test_expect_success 'atomic push works for two branches' '
@@ -55,9 +58,9 @@
 		test_commit two &&
 		git checkout second &&
 		test_commit three &&
-		git push --atomic up master second
+		git push --atomic up main second
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	test_refs second second
 '
 
@@ -70,7 +73,7 @@
 		test_commit two &&
 		git push --atomic --mirror up
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	test_refs second second
 '
 
@@ -79,7 +82,7 @@
 	(
 		cd workbench &&
 		test_commit one &&
-		git branch second master &&
+		git branch second main &&
 		test_commit two_a &&
 		git checkout second &&
 		test_commit two_b &&
@@ -87,36 +90,36 @@
 		test_commit four &&
 		git push --mirror up &&
 		# The actual test is below
-		git checkout master &&
+		git checkout main &&
 		test_commit three_a &&
 		git checkout second &&
 		git reset --hard HEAD^ &&
-		git push --force --atomic up master second
+		git push --force --atomic up main second
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	test_refs second second
 '
 
-# set up two branches where master can be pushed but second can not
+# set up two branches where main can be pushed but second can not
 # (non-fast-forward). Since second can not be pushed the whole operation
-# will fail and leave master untouched.
+# will fail and leave main untouched.
 test_expect_success 'atomic push fails if one branch fails' '
 	mk_repo_pair &&
 	(
 		cd workbench &&
 		test_commit one &&
-		git checkout -b second master &&
+		git checkout -b second main &&
 		test_commit two &&
 		test_commit three &&
 		test_commit four &&
 		git push --mirror up &&
 		git reset --hard HEAD~2 &&
 		test_commit five &&
-		git checkout master &&
+		git checkout main &&
 		test_commit six &&
 		test_must_fail git push --atomic --all up
 	) &&
-	test_refs master HEAD@{7} &&
+	test_refs main HEAD@{7} &&
 	test_refs second HEAD@{4}
 '
 
@@ -126,7 +129,7 @@
 	(
 		cd workbench &&
 		test_commit one &&
-		git checkout -b second master &&
+		git checkout -b second main &&
 		test_commit two &&
 		git push --mirror up
 	) &&
@@ -139,14 +142,14 @@
 	# see if we can now push both branches.
 	(
 		cd workbench &&
-		git checkout master &&
+		git checkout main &&
 		test_commit three &&
 		git checkout second &&
 		test_commit four &&
 		git tag test_tag &&
-		test_must_fail git push --tags --atomic up master second
+		test_must_fail git push --tags --atomic up main second
 	) &&
-	test_refs master HEAD@{3} &&
+	test_refs main HEAD@{3} &&
 	test_refs second HEAD@{1}
 '
 
@@ -155,7 +158,7 @@
 	(
 		cd workbench &&
 		test_commit one &&
-		git checkout -b second master &&
+		git checkout -b second main &&
 		test_commit two &&
 		git push --mirror up
 	) &&
@@ -165,19 +168,19 @@
 		HOOK="$HOOKDIR/update" &&
 		mkdir -p "$HOOKDIR" &&
 		write_script "$HOOK" <<-\EOF
-			# only allow update to master from now on
-			test "$1" = "refs/heads/master"
+			# only allow update to main from now on
+			test "$1" = "refs/heads/main"
 		EOF
 	) &&
 	(
 		cd workbench &&
-		git checkout master &&
+		git checkout main &&
 		test_commit three &&
 		git checkout second &&
 		test_commit four &&
-		test_must_fail git push --atomic up master second
+		test_must_fail git push --atomic up main second
 	) &&
-	test_refs master HEAD@{3} &&
+	test_refs main HEAD@{3} &&
 	test_refs second HEAD@{1}
 '
 
@@ -192,21 +195,21 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		test_must_fail git push --atomic up master
+		test_must_fail git push --atomic up main
 	) &&
-	test_refs master HEAD@{1}
+	test_refs main HEAD@{1}
 '
 
-# References in upstream : master(1) one(1) foo(1)
-# References in workbench: master(2)        foo(1) two(2) bar(2)
-# Atomic push            : master(2)               two(2) bar(2)
+# References in upstream : main(1) one(1) foo(1)
+# References in workbench: main(2)        foo(1) two(2) bar(2)
+# Atomic push            : main(2)               two(2) bar(2)
 test_expect_success 'atomic push reports (reject by update hook)' '
 	mk_repo_pair &&
 	(
 		cd workbench &&
 		test_commit one &&
 		git branch foo &&
-		git push up master one foo &&
+		git push up main one foo &&
 		git tag -d one
 	) &&
 	(
@@ -228,19 +231,19 @@
 		git branch bar
 	) &&
 	test_must_fail git -C workbench \
-		push --atomic up master two bar >out 2>&1 &&
+		push --atomic up main two bar >out 2>&1 &&
 	fmt_status_report <out >actual &&
 	cat >expect <<-EOF &&
 	To ../upstream
-	 ! [remote rejected] master -> master (atomic push failure)
+	 ! [remote rejected] main -> main (atomic push failure)
 	 ! [remote rejected] two -> two (atomic push failure)
 	 ! [remote rejected] bar -> bar (hook declined)
 	EOF
 	test_cmp expect actual
 '
 
-# References in upstream : master(1) one(1) foo(1)
-# References in workbench: master(2)        foo(1) two(2) bar(2)
+# References in upstream : main(1) one(1) foo(1)
+# References in workbench: main(2)        foo(1) two(2) bar(2)
 test_expect_success 'atomic push reports (mirror, but reject by update hook)' '
 	(
 		cd workbench &&
@@ -252,7 +255,7 @@
 	fmt_status_report <out >actual &&
 	cat >expect <<-EOF &&
 	To ../upstream
-	 ! [remote rejected] master -> master (atomic push failure)
+	 ! [remote rejected] main -> main (atomic push failure)
 	 ! [remote rejected] one (atomic push failure)
 	 ! [remote rejected] bar -> bar (hook declined)
 	 ! [remote rejected] two -> two (atomic push failure)
@@ -260,21 +263,21 @@
 	test_cmp expect actual
 '
 
-# References in upstream : master(2) one(1) foo(1)
-# References in workbench: master(1)        foo(1) two(2) bar(2)
+# References in upstream : main(2) one(1) foo(1)
+# References in workbench: main(1)        foo(1) two(2) bar(2)
 test_expect_success 'atomic push reports (reject by non-ff)' '
 	rm upstream/.git/hooks/update &&
 	(
 		cd workbench &&
-		git push up master &&
+		git push up main &&
 		git reset --hard HEAD^
 	) &&
 	test_must_fail git -C workbench \
-		push --atomic up master foo bar >out 2>&1 &&
+		push --atomic up main foo bar >out 2>&1 &&
 	fmt_status_report <out >actual &&
 	cat >expect <<-EOF &&
 	To ../upstream
-	 ! [rejected] master -> master (non-fast-forward)
+	 ! [rejected] main -> main (non-fast-forward)
 	 ! [rejected] bar -> bar (atomic push failed)
 	EOF
 	test_cmp expect actual
diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh
index 4357af1..dd5f44d 100755
--- a/t/t5544-pack-objects-hook.sh
+++ b/t/t5544-pack-objects-hook.sh
@@ -59,4 +59,14 @@
 	test_path_is_missing .git/hook.stdout
 '
 
+test_expect_success 'hook works with partial clone' '
+	clear_hook_results &&
+	test_config_global uploadpack.packObjectsHook ./hook &&
+	test_config_global uploadpack.allowFilter true &&
+	git clone --bare --no-local --filter=blob:none . dst.git &&
+	git -C dst.git rev-list --objects --missing=allow-any --no-object-names --all >objects &&
+	git -C dst.git cat-file --batch-check="%(objecttype)" <objects >types &&
+	! grep blob types
+'
+
 test_done
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 38e6f73..58c7add 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -2,6 +2,9 @@
 
 test_description='pushing to a repository using push options'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 mk_repo_pair () {
@@ -63,9 +66,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git push --push-option=asdf up master
+		git push --push-option=asdf up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	echo "asdf" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -79,9 +82,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		test_must_fail git push --push-option=asdf up master
+		test_must_fail git push --push-option=asdf up main
 	) &&
-	test_refs master HEAD@{1}
+	test_refs main HEAD@{1}
 '
 
 test_expect_success 'two push options work' '
@@ -92,9 +95,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git push --push-option=asdf --push-option="more structured text" up master
+		git push --push-option=asdf --push-option="more structured text" up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	printf "asdf\nmore structured text\n" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -123,14 +126,14 @@
 
 	git -C parent push \
 		--push-option=asdf --push-option="more structured text" \
-		--recurse-submodules=on-demand up master &&
+		--recurse-submodules=on-demand up main &&
 
-	git -C upstream rev-parse --verify master >expect &&
-	git -C parent/workbench rev-parse --verify master >actual &&
+	git -C upstream rev-parse --verify main >expect &&
+	git -C parent/workbench rev-parse --verify main >actual &&
 	test_cmp expect actual &&
 
-	git -C parent_upstream rev-parse --verify master >expect &&
-	git -C parent rev-parse --verify master >actual &&
+	git -C parent_upstream rev-parse --verify main >expect &&
+	git -C parent rev-parse --verify main >actual &&
 	test_cmp expect actual &&
 
 	printf "asdf\nmore structured text\n" >expect &&
@@ -148,9 +151,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git -c push.pushOption=default push up master
+		git -c push.pushOption=default push up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	echo "default" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -164,9 +167,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git -c push.pushOption=default1 -c push.pushOption=default2 push up master
+		git -c push.pushOption=default1 -c push.pushOption=default2 push up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	printf "default1\ndefault2\n" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -180,9 +183,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git -c push.pushOption=default push --push-option=manual up master
+		git -c push.pushOption=default push --push-option=manual up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	echo "manual" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -196,9 +199,9 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
+		git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up main
 	) &&
-	test_refs master master &&
+	test_refs main main &&
 	echo "default2" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
 	test_cmp expect upstream/.git/hooks/post-receive.push_options
@@ -212,16 +215,16 @@
 		test_commit one &&
 		git push --mirror up &&
 		test_commit two &&
-		test_must_fail git -c push.pushOption push up master
+		test_must_fail git -c push.pushOption push up main
 	) &&
-	test_refs master HEAD@{1}
+	test_refs main HEAD@{1}
 '
 
 test_expect_success 'push options keep quoted characters intact (direct)' '
 	mk_repo_pair &&
 	git -C upstream config receive.advertisePushOptions true &&
 	test_commit -C workbench one &&
-	git -C workbench push --push-option="\"embedded quotes\"" up master &&
+	git -C workbench push --push-option="\"embedded quotes\"" up main &&
 	echo "\"embedded quotes\"" >expect &&
 	test_cmp expect upstream/.git/hooks/pre-receive.push_options
 '
@@ -244,28 +247,28 @@
 test_expect_success 'push option denied properly by http server' '
 	mk_http_pair false &&
 	test_commit -C test_http_clone one &&
-	test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual &&
+	test_must_fail git -C test_http_clone push --push-option=asdf origin main 2>actual &&
 	test_i18ngrep "the receiving end does not support push options" actual &&
-	git -C test_http_clone push origin master
+	git -C test_http_clone push origin main
 '
 
 test_expect_success 'push options work properly across http' '
 	mk_http_pair true &&
 
 	test_commit -C test_http_clone one &&
-	git -C test_http_clone push origin master &&
-	git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
-	git -C test_http_clone rev-parse --verify master >actual &&
+	git -C test_http_clone push origin main &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify main >expect &&
+	git -C test_http_clone rev-parse --verify main >actual &&
 	test_cmp expect actual &&
 
 	test_commit -C test_http_clone two &&
-	git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master &&
+	git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin main &&
 	printf "asdf\nmore structured text\n" >expect &&
 	test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options &&
 	test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options &&
 
-	git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
-	git -C test_http_clone rev-parse --verify master >actual &&
+	git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify main >expect &&
+	git -C test_http_clone rev-parse --verify main >actual &&
 	test_cmp expect actual
 '
 
@@ -273,7 +276,7 @@
 	mk_http_pair true &&
 
 	test_commit -C test_http_clone one &&
-	git -C test_http_clone push --push-option="\"embedded quotes\"" origin master &&
+	git -C test_http_clone push --push-option="\"embedded quotes\"" origin main &&
 	echo "\"embedded quotes\"" >expect &&
 	test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
 '
diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh
index 1b19b3e..5a761f3 100755
--- a/t/t5548-push-porcelain.sh
+++ b/t/t5548-push-porcelain.sh
@@ -36,7 +36,7 @@
 		shift ||
 		return 1
 	done &&
-	git -C "$repo" update-ref refs/heads/master $oid
+	git -C "$repo" update-ref refs/heads/main $oid
 }
 
 # Format the output of git-push, git-show-ref and other commands to make a
@@ -57,8 +57,8 @@
 }
 
 setup_upstream_and_workbench () {
-	# Upstream  after setup : master(B)  foo(A)  bar(A)  baz(A)
-	# Workbench after setup : master(A)
+	# Upstream  after setup : main(B)  foo(A)  bar(A)  baz(A)
+	# Workbench after setup : main(A)
 	test_expect_success "setup upstream repository and workbench" '
 		rm -rf upstream.git workbench &&
 		git init --bare upstream.git &&
@@ -70,9 +70,9 @@
 			# this fixed-width oid will be replaced with "<OID>".
 			git config core.abbrev 7 &&
 			git remote add origin ../upstream.git &&
-			git update-ref refs/heads/master $A &&
+			git update-ref refs/heads/main $A &&
 			git push origin \
-				$B:refs/heads/master \
+				$B:refs/heads/main \
 				$A:refs/heads/foo \
 				$A:refs/heads/bar \
 				$A:refs/heads/baz
@@ -94,17 +94,17 @@
 		;;
 	esac
 
-	# Refs of upstream : master(B)  foo(A)  bar(A)  baz(A)
-	# Refs of workbench: master(A)                  baz(A)  next(A)
-	# git-push         : master(A)  NULL    (B)     baz(A)  next(A)
+	# Refs of upstream : main(B)  foo(A)  bar(A)  baz(A)
+	# Refs of workbench: main(A)                  baz(A)  next(A)
+	# git-push         : main(A)  NULL    (B)     baz(A)  next(A)
 	test_expect_success "porcelain output of successful git-push ($PROTOCOL)" '
 		(
 			cd workbench &&
-			git update-ref refs/heads/master $A &&
+			git update-ref refs/heads/main $A &&
 			git update-ref refs/heads/baz $A &&
 			git update-ref refs/heads/next $A &&
 			git push --porcelain --force origin \
-				master \
+				main \
 				:refs/heads/foo \
 				$B:bar \
 				baz \
@@ -116,7 +116,7 @@
 		=    refs/heads/baz:refs/heads/baz    [up to date]
 		     <COMMIT-B>:refs/heads/bar    <OID-A>..<OID-B>
 		-    :refs/heads/foo    [deleted]
-		+    refs/heads/master:refs/heads/master    <OID-B>...<OID-A> (forced update)
+		+    refs/heads/main:refs/heads/main    <OID-B>...<OID-A> (forced update)
 		*    refs/heads/next:refs/heads/next    [new branch]
 		Done
 		EOF
@@ -127,22 +127,22 @@
 		cat >expect <<-EOF &&
 		<COMMIT-B> refs/heads/bar
 		<COMMIT-A> refs/heads/baz
-		<COMMIT-A> refs/heads/master
+		<COMMIT-A> refs/heads/main
 		<COMMIT-A> refs/heads/next
 		EOF
 		test_cmp expect actual
 	'
 
-	# Refs of upstream : master(A)  bar(B)  baz(A)  next(A)
-	# Refs of workbench: master(B)  bar(A)  baz(A)  next(A)
-	# git-push         : master(B)  bar(A)  NULL    next(A)
+	# Refs of upstream : main(A)  bar(B)  baz(A)  next(A)
+	# Refs of workbench: main(B)  bar(A)  baz(A)  next(A)
+	# git-push         : main(B)  bar(A)  NULL    next(A)
 	test_expect_success "atomic push failed ($PROTOCOL)" '
 		(
 			cd workbench &&
-			git update-ref refs/heads/master $B &&
+			git update-ref refs/heads/main $B &&
 			git update-ref refs/heads/bar $A &&
 			test_must_fail git push --atomic --porcelain origin \
-				master \
+				main \
 				bar \
 				:baz \
 				next
@@ -153,7 +153,7 @@
 		=    refs/heads/next:refs/heads/next    [up to date]
 		!    refs/heads/bar:refs/heads/bar    [rejected] (non-fast-forward)
 		!    (delete):refs/heads/baz    [rejected] (atomic push failed)
-		!    refs/heads/master:refs/heads/master    [rejected] (atomic push failed)
+		!    refs/heads/main:refs/heads/main    [rejected] (atomic push failed)
 		Done
 		EOF
 		test_cmp expect actual &&
@@ -163,7 +163,7 @@
 		cat >expect <<-EOF &&
 		<COMMIT-B> refs/heads/bar
 		<COMMIT-A> refs/heads/baz
-		<COMMIT-A> refs/heads/master
+		<COMMIT-A> refs/heads/main
 		<COMMIT-A> refs/heads/next
 		EOF
 		test_cmp expect actual
@@ -174,16 +174,16 @@
 		EOF
 	'
 
-	# Refs of upstream : master(A)  bar(B)  baz(A)  next(A)
-	# Refs of workbench: master(B)  bar(A)  baz(A)  next(A)
-	# git-push         : master(B)  bar(A)  NULL    next(A)
+	# Refs of upstream : main(A)  bar(B)  baz(A)  next(A)
+	# Refs of workbench: main(B)  bar(A)  baz(A)  next(A)
+	# git-push         : main(B)  bar(A)  NULL    next(A)
 	test_expect_success "pre-receive hook declined ($PROTOCOL)" '
 		(
 			cd workbench &&
-			git update-ref refs/heads/master $B &&
+			git update-ref refs/heads/main $B &&
 			git update-ref refs/heads/bar $A &&
 			test_must_fail git push --porcelain --force origin \
-				master \
+				main \
 				bar \
 				:baz \
 				next
@@ -194,7 +194,7 @@
 		=    refs/heads/next:refs/heads/next    [up to date]
 		!    refs/heads/bar:refs/heads/bar    [remote rejected] (pre-receive hook declined)
 		!    :refs/heads/baz    [remote rejected] (pre-receive hook declined)
-		!    refs/heads/master:refs/heads/master    [remote rejected] (pre-receive hook declined)
+		!    refs/heads/main:refs/heads/main    [remote rejected] (pre-receive hook declined)
 		Done
 		EOF
 		test_cmp expect actual &&
@@ -204,7 +204,7 @@
 		cat >expect <<-EOF &&
 		<COMMIT-B> refs/heads/bar
 		<COMMIT-A> refs/heads/baz
-		<COMMIT-A> refs/heads/master
+		<COMMIT-A> refs/heads/main
 		<COMMIT-A> refs/heads/next
 		EOF
 		test_cmp expect actual
@@ -214,14 +214,14 @@
 		rm "$upstream/hooks/pre-receive"
 	'
 
-	# Refs of upstream : master(A)  bar(B)  baz(A)  next(A)
-	# Refs of workbench: master(B)  bar(A)  baz(A)  next(A)
-	# git-push         : master(B)  bar(A)  NULL    next(A)
+	# Refs of upstream : main(A)  bar(B)  baz(A)  next(A)
+	# Refs of workbench: main(B)  bar(A)  baz(A)  next(A)
+	# git-push         : main(B)  bar(A)  NULL    next(A)
 	test_expect_success "non-fastforward push ($PROTOCOL)" '
 		(
 			cd workbench &&
 			test_must_fail git push --porcelain origin \
-				master \
+				main \
 				bar \
 				:baz \
 				next
@@ -231,7 +231,7 @@
 		To <URL/of/upstream.git>
 		=    refs/heads/next:refs/heads/next    [up to date]
 		-    :refs/heads/baz    [deleted]
-		     refs/heads/master:refs/heads/master    <OID-A>..<OID-B>
+		     refs/heads/main:refs/heads/main    <OID-A>..<OID-B>
 		!    refs/heads/bar:refs/heads/bar    [rejected] (non-fast-forward)
 		Done
 		EOF
@@ -241,7 +241,7 @@
 		make_user_friendly_and_stable_output <out >actual &&
 		cat >expect <<-EOF &&
 		<COMMIT-B> refs/heads/bar
-		<COMMIT-B> refs/heads/master
+		<COMMIT-B> refs/heads/main
 		<COMMIT-A> refs/heads/next
 		EOF
 		test_cmp expect actual
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 483578b..6d9142a 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test dumb fetching over http via static file'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
@@ -26,7 +29,7 @@
 	 hooks/post-update
 	) &&
 	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	git push public master:master
+	git push public main:main
 '
 
 test_expect_success 'clone http repository' '
@@ -37,8 +40,8 @@
 
 test_expect_success 'list refs from outside any repository' '
 	cat >expect <<-EOF &&
-	$(git rev-parse master)	HEAD
-	$(git rev-parse master)	refs/heads/master
+	$(git rev-parse main)	HEAD
+	$(git rev-parse main)	refs/heads/main
 	EOF
 	nongit git ls-remote "$HTTPD_URL/dumb/repo.git" >actual &&
 	test_cmp expect actual
@@ -181,8 +184,8 @@
 
 	HEAD=$(git rev-parse --verify HEAD) &&
 	(cd clone2 &&
-	 git http-fetch -a -w heads/master-new $HEAD $(git config remote.origin.url) &&
-	 git checkout master-new &&
+	 git http-fetch -a -w heads/main-new $HEAD $(git config remote.origin.url) &&
+	 git checkout main-new &&
 	 test $HEAD = $(git rev-parse --verify HEAD)) &&
 	test_cmp file clone2/file
 '
@@ -192,19 +195,19 @@
 
 	HEAD=$(git rev-parse --verify HEAD) &&
 	(cd clone3 &&
-	 git http-fetch -w heads/master-new $HEAD $(git config remote.origin.url) &&
-	 git checkout master-new &&
+	 git http-fetch -w heads/main-new $HEAD $(git config remote.origin.url) &&
+	 git checkout main-new &&
 	 test $HEAD = $(git rev-parse --verify HEAD)) &&
 	test_cmp file clone3/file
 '
 
 test_expect_success 'http remote detects correct HEAD' '
-	git push public master:other &&
+	git push public main:other &&
 	(cd clone &&
 	 git remote set-head origin -d &&
 	 git remote set-head origin -a &&
 	 git symbolic-ref refs/remotes/origin/HEAD > output &&
-	 echo refs/remotes/origin/master > expect &&
+	 echo refs/remotes/origin/main > expect &&
 	 test_cmp expect output
 	)
 '
@@ -224,7 +227,10 @@
 
 	git init packfileclient &&
 	p=$(cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git && ls objects/pack/pack-*.pack) &&
-	git -C packfileclient http-fetch --packfile=$ARBITRARY "$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
+	git -C packfileclient http-fetch --packfile=$ARBITRARY \
+		--index-pack-arg=index-pack --index-pack-arg=--stdin \
+		--index-pack-arg=--keep \
+		"$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
 
 	grep "^keep.[0-9a-f]\{16,\}$" out &&
 	cut -c6- out >packhash &&
@@ -416,7 +422,7 @@
 	evil=$HTTPD_DOCUMENT_ROOT_PATH/evil.git &&
 	git init --bare "$evil" &&
 	# do this by hand to avoid object existence check
-	printf "%s\\t%s\\n" $sha1 refs/heads/master >"$evil/info/refs"
+	printf "%s\\t%s\\n" $sha1 refs/heads/main >"$evil/info/refs"
 '
 
 # Here we'll just redirect via HTTP. In a real-world attack these would be on
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index e40e9ed..984dba2 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test smart fetching over http via http-backend'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
@@ -18,7 +21,7 @@
 	 git --bare init
 	) &&
 	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	git push public master:master
+	git push public main:main
 '
 
 setup_askpass_helper
@@ -238,9 +241,9 @@
 
 test_expect_success 'create namespaced refs' '
 	test_commit namespaced &&
-	git push public HEAD:refs/namespaces/ns/refs/heads/master &&
+	git push public HEAD:refs/namespaces/ns/refs/heads/main &&
 	git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-		symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master
+		symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/main
 '
 
 test_expect_success 'smart clone respects namespace' '
@@ -271,7 +274,7 @@
 	EOF
 	git config http.cookiefile cookies.txt &&
 	git config http.savecookies true &&
-	git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
+	git ls-remote $HTTPD_URL/smart_cookies/repo.git main &&
 
 	# NEEDSWORK: If the overspecification of the expected result is reduced, we
 	# might be able to run this test in all protocol versions.
@@ -344,12 +347,12 @@
 test_expect_success 'test allowreachablesha1inwant' '
 	test_when_finished "rm -rf test_reachable.git" &&
 	server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	master_sha=$(git -C "$server" rev-parse refs/heads/master) &&
+	main_sha=$(git -C "$server" rev-parse refs/heads/main) &&
 	git -C "$server" config uploadpack.allowreachablesha1inwant 1 &&
 
 	git init --bare test_reachable.git &&
 	git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" &&
-	git -C test_reachable.git fetch origin "$master_sha"
+	git -C test_reachable.git fetch origin "$main_sha"
 '
 
 test_expect_success 'test allowreachablesha1inwant with unreachable' '
@@ -363,7 +366,7 @@
 	git push public :refs/heads/doomed &&
 
 	server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	master_sha=$(git -C "$server" rev-parse refs/heads/master) &&
+	main_sha=$(git -C "$server" rev-parse refs/heads/main) &&
 	git -C "$server" config uploadpack.allowreachablesha1inwant 1 &&
 
 	git init --bare test_reachable.git &&
@@ -385,7 +388,7 @@
 	git push public :refs/heads/doomed &&
 
 	server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	master_sha=$(git -C "$server" rev-parse refs/heads/master) &&
+	main_sha=$(git -C "$server" rev-parse refs/heads/main) &&
 	git -C "$server" config uploadpack.allowreachablesha1inwant 1 &&
 
 	git init --bare test_reachable.git &&
@@ -444,8 +447,8 @@
 	test_commit -C "$SERVER" bar &&
 	git -C client -c protocol.version=0 fetch &&
 
-	git -C "$SERVER" rev-parse master >expect &&
-	git -C client rev-parse origin/master >actual &&
+	git -C "$SERVER" rev-parse main >expect &&
+	git -C client rev-parse origin/main >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
index 156c704..7b9fb4f 100755
--- a/t/t5552-skipping-fetch-negotiator.sh
+++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -100,7 +100,7 @@
 	git -C server checkout --orphan anotherbranch &&
 	test_commit -C server to_fetch &&
 
-	# The server advertising "c3" (as "refs/heads/master") means that we do
+	# The server advertising "c3" (as "refs/heads/main") means that we do
 	# not need to send any ancestors of "c3", but we still need to send "c3"
 	# itself.
 	test_config -C client fetch.negotiationalgorithm skipping &&
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index 7622981..b1d614c 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='"git fetch/pull --set-upstream" basic tests.'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_config () {
@@ -32,9 +35,9 @@
 	git remote add upstream parent
 '
 
-test_expect_success 'setup commit on master and other fetch' '
+test_expect_success 'setup commit on main and other fetch' '
 	test_commit one &&
-	git push upstream master &&
+	git push upstream main &&
 	git checkout -b other &&
 	test_commit two &&
 	git push upstream other
@@ -43,38 +46,38 @@
 # tests for fetch --set-upstream
 
 test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '
-	clear_config master other &&
-	git checkout master &&
+	clear_config main other &&
+	git checkout main &&
 	git fetch --set-upstream upstream &&
-	check_config_missing master &&
+	check_config_missing main &&
 	check_config_missing other
 '
 
-test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '
-	clear_config master other &&
-	git fetch --set-upstream upstream master &&
-	check_config master upstream refs/heads/master &&
+test_expect_success 'fetch --set-upstream upstream main sets branch main but not other' '
+	clear_config main other &&
+	git fetch --set-upstream upstream main &&
+	check_config main upstream refs/heads/main &&
 	check_config_missing other
 '
 
 test_expect_success 'fetch --set-upstream upstream other sets branch other' '
-	clear_config master other &&
+	clear_config main other &&
 	git fetch --set-upstream upstream other &&
-	check_config master upstream refs/heads/other &&
+	check_config main upstream refs/heads/other &&
 	check_config_missing other
 '
 
-test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '
+test_expect_success 'fetch --set-upstream main:other does not set the branch other2' '
 	clear_config other2 &&
-	git fetch --set-upstream upstream master:other2 &&
+	git fetch --set-upstream upstream main:other2 &&
 	check_config_missing other2
 '
 
 test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '
-	# master explicitly not cleared, we check that it is not touched from previous value
+	# main explicitly not cleared, we check that it is not touched from previous value
 	clear_config other other2 &&
 	test_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&
-	check_config master upstream refs/heads/other &&
+	check_config main upstream refs/heads/other &&
 	check_config_missing other &&
 	check_config_missing other2
 '
@@ -83,7 +86,7 @@
 	clear_config other other2 &&
 	url="file://$PWD" &&
 	git fetch --set-upstream "$url" &&
-	check_config master "$url" HEAD &&
+	check_config main "$url" HEAD &&
 	check_config_missing other &&
 	check_config_missing other2
 '
@@ -96,30 +99,30 @@
 	git remote add upstream parent
 '
 
-test_expect_success 'setup commit on master and other pull' '
+test_expect_success 'setup commit on main and other pull' '
 	test_commit three &&
-	git push --tags upstream master &&
+	git push --tags upstream main &&
 	test_commit four &&
 	git push upstream other
 '
 
-test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '
-	clear_config master other &&
-	git pull --set-upstream upstream master &&
-	check_config master upstream refs/heads/master &&
+test_expect_success 'pull --set-upstream upstream main sets branch main but not other' '
+	clear_config main other &&
+	git pull --set-upstream upstream main &&
+	check_config main upstream refs/heads/main &&
 	check_config_missing other
 '
 
-test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '
+test_expect_success 'pull --set-upstream main:other2 does not set the branch other2' '
 	clear_config other2 &&
-	git pull --set-upstream upstream master:other2 &&
+	git pull --set-upstream upstream main:other2 &&
 	check_config_missing other2
 '
 
-test_expect_success 'pull --set-upstream upstream other sets branch master' '
-	clear_config master other &&
+test_expect_success 'pull --set-upstream upstream other sets branch main' '
+	clear_config main other &&
 	git pull --set-upstream upstream other &&
-	check_config master upstream refs/heads/other &&
+	check_config main upstream refs/heads/other &&
 	check_config_missing other
 '
 
@@ -130,47 +133,47 @@
 '
 
 test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '
-	# master explicitly not cleared, we check that it is not touched from previous value
+	# main explicitly not cleared, we check that it is not touched from previous value
 	clear_config other other2 three &&
 	test_must_fail git pull --set-upstream http://nosuchdomain.example.com &&
-	check_config master upstream refs/heads/other &&
+	check_config main upstream refs/heads/other &&
 	check_config_missing other &&
 	check_config_missing other2 &&
 	check_config_missing three
 '
 
 test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '
-	clear_config master other &&
+	clear_config main other &&
 	git pull --set-upstream upstream HEAD &&
-	check_config master upstream HEAD &&
+	check_config main upstream HEAD &&
 	git checkout other &&
 	git pull --set-upstream upstream HEAD &&
 	check_config other upstream HEAD
 '
 
 test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '
-	clear_config master three &&
-	git pull --set-upstream upstream master three &&
-	check_config_missing master &&
+	clear_config main three &&
+	git pull --set-upstream upstream main three &&
+	check_config_missing main &&
 	check_config_missing three
 '
 
 test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '
-	clear_config master other other2 &&
-	git checkout master &&
+	clear_config main other other2 &&
+	git checkout main &&
 	url="file://$PWD" &&
 	git pull --set-upstream "$url" &&
-	check_config master "$url" HEAD &&
+	check_config main "$url" HEAD &&
 	check_config_missing other &&
 	check_config_missing other2
 '
 
 test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '
-	clear_config master other other2 &&
-	git checkout master &&
+	clear_config main other other2 &&
+	git checkout main &&
 	url="file://$PWD" &&
-	git pull --set-upstream "$url" master &&
-	check_config master "$url" refs/heads/master &&
+	git pull --set-upstream "$url" main &&
+	check_config main "$url" refs/heads/main &&
 	check_config_missing other &&
 	check_config_missing other2
 '
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
index 9fafcf1..d30cf4f 100755
--- a/t/t5560-http-backend-noserver.sh
+++ b/t/t5560-http-backend-noserver.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test git-http-backend-noserver'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
index 6eb0294..9c57d84 100755
--- a/t/t5561-http-backend.sh
+++ b/t/t5561-http-backend.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test git-http-backend'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 
@@ -39,9 +42,9 @@
 
 grep '^[^#]' >exp <<EOF
 
-###  refs/heads/master
+###  refs/heads/main
 ###
-GET  /smart/repo.git/refs/heads/master HTTP/1.1 404 -
+GET  /smart/repo.git/refs/heads/main HTTP/1.1 404 -
 
 ###  getanyfile default
 ###
diff --git a/t/t556x_common b/t/t556x_common
index 359fcfe..670fb89 100755
--- a/t/t556x_common
+++ b/t/t556x_common
@@ -22,7 +22,7 @@
 	 : >objects/info/http-alternates
 	) &&
 	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	git push public master:master &&
+	git push public main:main &&
 
 	(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 	 git repack -a -d
@@ -31,7 +31,7 @@
 	echo other >file &&
 	git add file &&
 	git commit -m two &&
-	git push public master:master &&
+	git push public main:main &&
 
 	LOOSE_URL=$(find_file objects/??) &&
 	PACK_URL=$(find_file objects/pack/*.pack) &&
@@ -51,8 +51,8 @@
 
 SMART=smart
 GIT_HTTP_EXPORT_ALL=1 && export GIT_HTTP_EXPORT_ALL
-test_expect_success 'direct refs/heads/master not found' '
-	GET refs/heads/master "404 Not Found"
+test_expect_success 'direct refs/heads/main not found' '
+	GET refs/heads/main "404 Not Found"
 '
 test_expect_success 'static file is ok' '
 	get_static_files "200 OK"
diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh
index 0fbb194..82c31ab 100755
--- a/t/t5570-git-daemon.sh
+++ b/t/t5570-git-daemon.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test fetching over git protocol'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-git-daemon.sh
@@ -26,7 +29,7 @@
 	 : >git-daemon-export-ok
 	) &&
 	git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" &&
-	git push public master:master
+	git push public main:main
 '
 
 test_expect_success 'clone git repository' '
@@ -55,12 +58,12 @@
 '
 
 test_expect_success 'remote detects correct HEAD' '
-	git push public master:other &&
+	git push public main:other &&
 	(cd clone &&
 	 git remote set-head -d origin &&
 	 git remote set-head -a origin &&
 	 git symbolic-ref refs/remotes/origin/HEAD > output &&
-	 echo refs/remotes/origin/master > expect &&
+	 echo refs/remotes/origin/main > expect &&
 	 test_cmp expect output
 	)
 '
@@ -153,7 +156,7 @@
 
 msg="access denied or repository not exported"
 test_expect_success 'clone non-existent' "test_remote_error    '$msg' clone nowhere.git"
-test_expect_success 'push disabled'      "test_remote_error    '$msg' push  repo.git master"
+test_expect_success 'push disabled'      "test_remote_error    '$msg' push  repo.git main"
 test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git"
 test_expect_success 'not exported'       "test_remote_error -n '$msg' fetch repo.git"
 
@@ -161,7 +164,7 @@
 start_git_daemon --informative-errors
 
 test_expect_success 'clone non-existent' "test_remote_error    'no such repository'      clone nowhere.git"
-test_expect_success 'push disabled'      "test_remote_error    'service not enabled'     push  repo.git master"
+test_expect_success 'push disabled'      "test_remote_error    'service not enabled'     push  repo.git main"
 test_expect_success 'read access denied' "test_remote_error -x 'no such repository'      fetch repo.git"
 test_expect_success 'not exported'       "test_remote_error -n 'repository not exported' fetch repo.git"
 
@@ -197,10 +200,10 @@
 	fake_nc "$GIT_DAEMON_HOST_PORT" <input >output &&
 	depacketize <output >output.raw &&
 
-	# just pick out the value of master, which avoids any protocol
+	# just pick out the value of main, which avoids any protocol
 	# particulars
-	perl -lne "print \$1 if m{^(\\S+) refs/heads/master}" <output.raw >actual &&
-	git -C "$repo" rev-parse master >expect &&
+	perl -lne "print \$1 if m{^(\\S+) refs/heads/main}" <output.raw >actual &&
+	git -C "$repo" rev-parse main >expect &&
 	test_cmp expect actual
 '
 
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
index ac53d63..ad8d580 100755
--- a/t/t5571-pre-push-hook.sh
+++ b/t/t5571-pre-push-hook.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='check pre-push hooks'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Setup hook that always succeeds
@@ -48,11 +51,11 @@
 cat >expected <<EOF
 parent1
 repo1
-refs/heads/master $COMMIT2 refs/heads/foreign $COMMIT1
+refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1
 EOF
 
 test_expect_success 'push with hook' '
-	git push parent1 master:foreign &&
+	git push parent1 main:foreign &&
 	diff expected actual
 '
 
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 37fd06b..29537f4 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -170,7 +170,7 @@
 	# create topic branch in clone, not based on any remote-tracking branch
 	git -C superclone checkout -b feat HEAD~1 &&
 	test_commit -C superclone first_on_feat &&
-	git -C superclone pull --rebase --recurse-submodules origin master
+	git -C superclone pull --rebase --recurse-submodules origin HEAD
 '
 
 # NOTE:
@@ -200,8 +200,8 @@
 
 	git clone parent child &&
 
-	# Reset master so that it has no merge base with
-	# refs/remotes/origin/master.
+	# Reset the current branch so that it has no merge base with
+	# the remote-tracking branch.
 	OTHER=$(git -C child commit-tree -m bar \
 		$(git -C child rev-parse HEAD^{tree})) &&
 	git -C child reset --hard "$OTHER" &&
diff --git a/t/t5580-unc-paths.sh b/t/t5580-unc-paths.sh
index cf768b3..cd803ae 100755
--- a/t/t5580-unc-paths.sh
+++ b/t/t5580-unc-paths.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='various Windows-only path tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if test_have_prereq CYGWIN
@@ -53,7 +56,7 @@
 	git init to-fetch &&
 	(
 		cd to-fetch &&
-		git fetch "$UNCPATH" master
+		git fetch "$UNCPATH" main
 	)
 '
 
diff --git a/t/t5581-http-curl-verbose.sh b/t/t5581-http-curl-verbose.sh
index 927aad0..cded79c 100755
--- a/t/t5581-http-curl-verbose.sh
+++ b/t/t5581-http-curl-verbose.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test GIT_CURL_VERBOSE'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
@@ -13,7 +16,7 @@
 	git add file &&
 	git commit -m one &&
 	git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-	git push public master:master
+	git push public main:main
 '
 
 test_expect_success 'failure in git-upload-pack is shown' '
diff --git a/t/t5582-fetch-negative-refspec.sh b/t/t5582-fetch-negative-refspec.sh
index 2f3b064..f345097 100755
--- a/t/t5582-fetch-negative-refspec.sh
+++ b/t/t5582-fetch-negative-refspec.sh
@@ -5,6 +5,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -22,12 +25,12 @@
 		git switch -c alternate &&
 		echo >file updated again by one &&
 		git commit -a -m "updated by one again" &&
-		git switch master
+		git switch main
 	) &&
 	git clone . two &&
 	(
 		cd two &&
-		git config branch.master.remote one &&
+		git config branch.main.remote one &&
 		git config remote.one.url ../one/.git/ &&
 		git config remote.one.fetch +refs/heads/*:refs/remotes/one/* &&
 		git config --add remote.one.fetch ^refs/heads/alternate
@@ -43,9 +46,9 @@
 		test_must_fail git rev-parse --verify refs/remotes/one/alternate &&
 		git fetch one &&
 		test_must_fail git rev-parse --verify refs/remotes/one/alternate &&
-		git rev-parse --verify refs/remotes/one/master &&
-		mine=$(git rev-parse refs/remotes/one/master) &&
-		his=$(cd ../one && git rev-parse refs/heads/master) &&
+		git rev-parse --verify refs/remotes/one/main &&
+		mine=$(git rev-parse refs/remotes/one/main) &&
+		his=$(cd ../one && git rev-parse refs/heads/main) &&
 		test "z$mine" = "z$his"
 	)
 '
@@ -57,7 +60,7 @@
 		cd three &&
 		alternate_in_one=$(cd ../one && git rev-parse refs/heads/alternate) &&
 		echo $alternate_in_one >expect &&
-		git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^refs/heads/master &&
+		git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^refs/heads/main &&
 		cut -f -1 .git/FETCH_HEAD >actual &&
 		test_cmp expect actual
 	)
@@ -68,8 +71,8 @@
 	git commit -a -m "updated by origin yet again" &&
 	(
 		cd three &&
-		master_in_one=$(cd ../one && git rev-parse refs/heads/master) &&
-		test_must_fail git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^$master_in_one
+		main_in_one=$(cd ../one && git rev-parse refs/heads/main) &&
+		test_must_fail git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^$main_in_one
 	)
 '
 
@@ -92,10 +95,10 @@
 	(
 		cd three &&
 		alternate_in_one=$(cd ../one && git rev-parse refs/heads/alternate) &&
-		master_in_one=$(cd ../one && git rev-parse refs/heads/master) &&
+		main_in_one=$(cd ../one && git rev-parse refs/heads/main) &&
 		echo $alternate_in_one >expect &&
-		echo $master_in_one >>expect &&
-		git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^master &&
+		echo $main_in_one >>expect &&
+		git fetch ../one/.git refs/heads/*:refs/remotes/one/* ^main &&
 		cut -f -1 .git/FETCH_HEAD >actual &&
 		test_cmp expect actual
 	)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 7df3c53..e7e6c08 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -2,6 +2,9 @@
 
 test_description=clone
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 X=
@@ -37,7 +40,7 @@
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'output from clone' '
+test_expect_success 'output from clone' '
 	rm -fr dst &&
 	git clone -n "file://$(pwd)/src" dst >output 2>&1 &&
 	test $(grep Clon output | wc -l) = 1
@@ -217,7 +220,7 @@
 		rm -fr dst &&
 		git clone src dst &&
 		cd dst &&
-		actual="z$(git config branch.master.rebase)" &&
+		actual="z$(git config branch.main.rebase)" &&
 		test ztrue = $actual
 	)
 '
@@ -591,7 +594,7 @@
 
 	(
 		cd src &&
-		git checkout -b another master
+		git checkout -b another main
 	) &&
 	git clone src target-11 &&
 	test "z$( cd target-11 && git symbolic-ref HEAD )" = zrefs/heads/another
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 2f7be23..e845d62 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='test clone --reference'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 base_dir=$(pwd)
@@ -49,7 +52,7 @@
 '
 
 test_expect_success 'pulling from reference' '
-	git -C C pull ../B master
+	git -C C pull ../B main
 '
 
 test_expect_success 'that reference gets used' '
@@ -70,7 +73,7 @@
 '
 
 test_expect_success 'pulling from reference' '
-	git -C D pull ../B master
+	git -C D pull ../B main
 '
 
 test_expect_success 'that reference gets used' '
@@ -136,11 +139,11 @@
 	git clone A J &&
 	(
 		cd J &&
-		git checkout -b other master^ &&
+		git checkout -b other main^ &&
 		echo other >otherfile &&
 		git add otherfile &&
 		git commit -m other &&
-		git checkout master
+		git checkout main
 	)
 '
 
@@ -152,9 +155,9 @@
 		git remote add J "file://$base_dir/J" &&
 		GIT_TRACE_PACKET=$U.K git fetch J
 	) &&
-	master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
+	main_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/main) &&
 	test -s "$U.K" &&
-	! grep " want $master_object" "$U.K" &&
+	! grep " want $main_object" "$U.K" &&
 	tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
 	! grep " want $tag_object" "$U.K"
 '
@@ -326,7 +329,7 @@
 	for raw in $(ls T*.raw)
 	do
 		sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
-		    -e "/multi-pack-index/d" <$raw >$raw.de-sha-1 &&
+		    -e "/multi-pack-index/d" -e "/rev/d" <$raw >$raw.de-sha-1 &&
 		sort $raw.de-sha-1 >$raw.de-sha || return 1
 	done &&
 
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index af23419..7d63365 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test local clone'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 repo_is_hardlinked() {
@@ -15,7 +18,7 @@
 	test "$(cd a.git && git config --bool core.bare)" = true &&
 	test "$(cd x && git config --bool core.bare)" = true &&
 	git bundle create b1.bundle --all &&
-	git bundle create b2.bundle master &&
+	git bundle create b2.bundle main &&
 	mkdir dir &&
 	cp b1.bundle dir/b3 &&
 	cp b1.bundle b4
@@ -84,7 +87,7 @@
 	git clone b2.bundle b2 &&
 	(cd b2 &&
 	git fetch &&
-	test_must_fail git rev-parse --verify refs/heads/master)
+	test_must_fail git rev-parse --verify refs/heads/main)
 '
 
 test_expect_success 'clone empty repository' '
@@ -98,9 +101,9 @@
 	 echo "content" >> foo &&
 	 git add foo &&
 	 git commit -m "Initial commit" &&
-	 git push origin master &&
-	 expected=$(git rev-parse master) &&
-	 actual=$(git --git-dir=../empty/.git rev-parse master) &&
+	 git push origin main &&
+	 expected=$(git rev-parse main) &&
+	 actual=$(git --git-dir=../empty/.git rev-parse main) &&
 	 test $actual = $expected)
 '
 
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index 7f082fb..52e5789 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='basic clone options'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -15,7 +18,7 @@
 test_expect_success 'clone -o' '
 
 	git clone -o foo parent clone-o &&
-	git -C clone-o rev-parse --verify refs/remotes/foo/master
+	git -C clone-o rev-parse --verify refs/remotes/foo/main
 
 '
 
@@ -45,7 +48,7 @@
 test_expect_success 'uses "origin" for default remote name' '
 
 	git clone parent clone-default-origin &&
-	git -C clone-default-origin rev-parse --verify refs/remotes/origin/master
+	git -C clone-default-origin rev-parse --verify refs/remotes/origin/main
 
 '
 
@@ -74,14 +77,14 @@
 
 	test_config_global clone.defaultRemoteName from_config &&
 	git clone parent clone-config-origin &&
-	git -C clone-config-origin rev-parse --verify refs/remotes/from_config/master
+	git -C clone-config-origin rev-parse --verify refs/remotes/from_config/main
 
 '
 
 test_expect_success 'prefers --origin over -c config' '
 
 	git clone -c clone.defaultRemoteName=inline --origin from_option parent clone-o-and-inline-config &&
-	git -C clone-o-and-inline-config rev-parse --verify refs/remotes/from_option/master
+	git -C clone-o-and-inline-config rev-parse --verify refs/remotes/from_option/main
 
 '
 
@@ -102,11 +105,13 @@
 '
 
 test_expect_success 'chooses correct default initial branch name' '
-	git init --bare empty &&
+	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+	git -c init.defaultBranch=foo init --bare empty &&
+	test_config -C empty lsrefs.unborn advertise &&
 	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
 	git -c init.defaultBranch=up clone empty whats-up &&
-	test refs/heads/up = $(git -C whats-up symbolic-ref HEAD) &&
-	test refs/heads/up = $(git -C whats-up config branch.up.merge)
+	test refs/heads/foo = $(git -C whats-up symbolic-ref HEAD) &&
+	test refs/heads/foo = $(git -C whats-up config branch.foo.merge)
 '
 
 test_expect_success 'guesses initial branch name correctly' '
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 26985f4..f4c383c 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='some bundle related tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -19,7 +22,7 @@
 '
 
 test_expect_success '"verify" needs a worktree' '
-	git bundle create tip.bundle -1 master &&
+	git bundle create tip.bundle -1 main &&
 	nongit test_must_fail git bundle verify ../tip.bundle 2>err &&
 	test_i18ngrep "need a repository" err
 '
@@ -38,16 +41,16 @@
 	test_must_fail git bundle create adir --all
 '
 
-test_expect_failure 'bundle --stdin' '
-	echo master | git bundle create stdin-bundle.bdl --stdin &&
+test_expect_success 'bundle --stdin' '
+	echo main | git bundle create stdin-bundle.bdl --stdin &&
 	git ls-remote stdin-bundle.bdl >output &&
-	grep master output
+	grep main output
 '
 
-test_expect_failure 'bundle --stdin <rev-list options>' '
-	echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+test_expect_success 'bundle --stdin <rev-list options>' '
+	echo main | git bundle create hybrid-bundle.bdl --stdin tag &&
 	git ls-remote hybrid-bundle.bdl >output &&
-	grep master output
+	grep main output
 '
 
 test_expect_success 'empty bundle file is rejected' '
@@ -83,14 +86,14 @@
 
 test_expect_success 'failed bundle creation does not leave cruft' '
 	# This fails because the bundle would be empty.
-	test_must_fail git bundle create fail.bundle master..master &&
+	test_must_fail git bundle create fail.bundle main..main &&
 	test_path_is_missing fail.bundle.lock
 '
 
 test_expect_success 'fetch SHA-1 from bundle' '
 	test_create_repo foo &&
 	test_commit -C foo x &&
-	git -C foo bundle create tip.bundle -1 master &&
+	git -C foo bundle create tip.bundle -1 main &&
 	git -C foo rev-parse HEAD >hash &&
 
 	# Exercise to ensure that fetching a SHA-1 from a bundle works with no
diff --git a/t/t5608-clone-2gb.sh b/t/t5608-clone-2gb.sh
index 4c476d2..87a8cd9 100755
--- a/t/t5608-clone-2gb.sh
+++ b/t/t5608-clone-2gb.sh
@@ -26,7 +26,7 @@
 		i=$(($i+1)) ||
 		echo $? > exit-status
 	 done &&
-	 echo "commit refs/heads/master" &&
+	 echo "commit refs/heads/main" &&
 	 echo "author A U Thor <author@email.com> 123456789 +0000" &&
 	 echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
 	 echo "data 5" &&
diff --git a/t/t5609-clone-branch.sh b/t/t5609-clone-branch.sh
index 6e7a7be..f86a674 100755
--- a/t/t5609-clone-branch.sh
+++ b/t/t5609-clone-branch.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='clone --branch option'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_HEAD() {
@@ -20,7 +23,7 @@
 	 echo one >file && git add file && git commit -m one &&
 	 git checkout -b two &&
 	 echo two >file && git add file && git commit -m two &&
-	 git checkout master) &&
+	 git checkout main) &&
 	mkdir empty &&
 	(cd empty && git init)
 '
@@ -28,7 +31,7 @@
 test_expect_success 'vanilla clone chooses HEAD' '
 	git clone parent clone &&
 	(cd clone &&
-	 check_HEAD master &&
+	 check_HEAD main &&
 	 check_file one
 	)
 '
@@ -53,7 +56,7 @@
 
 test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
 	(cd clone-two &&
-	 echo refs/remotes/origin/master >expect &&
+	 echo refs/remotes/origin/main >expect &&
 	 git symbolic-ref refs/remotes/origin/HEAD >actual &&
 	 test_cmp expect actual
 	)
diff --git a/t/t5610-clone-detached.sh b/t/t5610-clone-detached.sh
index 8b0d607..a7ec21e 100755
--- a/t/t5610-clone-detached.sh
+++ b/t/t5610-clone-detached.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test cloning a repository with detached HEAD'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 head_is_detached() {
@@ -20,7 +23,7 @@
 '
 
 test_expect_success 'clone repo (detached HEAD points to branch)' '
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git clone "file://$PWD" detached-branch
 '
 test_expect_success 'cloned HEAD matches' '
@@ -59,7 +62,7 @@
 '
 
 test_expect_success 'clone repo (orphan detached HEAD)' '
-	git checkout master^0 &&
+	git checkout main^0 &&
 	echo four >file &&
 	git commit -a -m four &&
 	git clone "file://$PWD" detached-orphan
diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh
index 8e0fd39..9f555b8 100755
--- a/t/t5611-clone-config.sh
+++ b/t/t5611-clone-config.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='tests for git clone -c key=value'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'clone -c sets config in cloned repo' '
@@ -47,16 +50,16 @@
 
 test_expect_success 'clone -c remote.origin.fetch=<refspec> works' '
 	rm -rf child &&
-	git update-ref refs/grab/it refs/heads/master &&
-	git update-ref refs/leave/out refs/heads/master &&
+	git update-ref refs/grab/it refs/heads/main &&
+	git update-ref refs/leave/out refs/heads/main &&
 	git clone -c "remote.origin.fetch=+refs/grab/*:refs/grab/*" . child &&
 	git -C child for-each-ref --format="%(refname)" >actual &&
 
 	cat >expect <<-\EOF &&
 	refs/grab/it
-	refs/heads/master
+	refs/heads/main
 	refs/remotes/origin/HEAD
-	refs/remotes/origin/master
+	refs/remotes/origin/main
 	EOF
 	test_cmp expect actual
 '
@@ -68,9 +71,9 @@
 
 	cat >expect <<-\EOF &&
 	refs/grab/it
-	refs/heads/master
+	refs/heads/main
 	refs/remotes/origin/HEAD
-	refs/remotes/origin/master
+	refs/remotes/origin/main
 	EOF
 	test_cmp expect actual
 '
@@ -85,9 +88,9 @@
 
 	cat >expect <<-\EOF &&
 	refs/grab/it
-	refs/heads/master
+	refs/heads/main
 	refs/remotes/upstream/HEAD
-	refs/remotes/upstream/master
+	refs/remotes/upstream/main
 	EOF
 	test_cmp expect actual
 '
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index e3b436d..6a6af74 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -1,10 +1,13 @@
 #!/bin/sh
 
 test_description='test refspec written by clone-command'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-	# Make two branches, "master" and "side"
+	# Make two branches, "main" and "side"
 	echo one >file &&
 	git add file &&
 	git commit -m one &&
@@ -16,7 +19,7 @@
 	git checkout -b side &&
 	echo four >file &&
 	git commit -a -m four &&
-	git checkout master &&
+	git checkout main &&
 	git tag five &&
 
 	# default clone
@@ -25,18 +28,18 @@
 	# default clone --no-tags
 	git clone --no-tags . dir_all_no_tags &&
 
-	# default --single that follows HEAD=master
-	git clone --single-branch . dir_master &&
+	# default --single that follows HEAD=main
+	git clone --single-branch . dir_main &&
 
-	# default --single that follows HEAD=master with no tags
-	git clone --single-branch --no-tags . dir_master_no_tags &&
+	# default --single that follows HEAD=main with no tags
+	git clone --single-branch --no-tags . dir_main_no_tags &&
 
 	# default --single that follows HEAD=side
 	git checkout side &&
 	git clone --single-branch . dir_side &&
 
 	# explicit --single that follows side
-	git checkout master &&
+	git checkout main &&
 	git clone --single-branch --branch side . dir_side2 &&
 
 	# default --single with --mirror
@@ -55,11 +58,11 @@
 	# explicit --single with tag and --no-tags
 	git clone --single-branch --no-tags --branch two . dir_tag_no_tags &&
 
-	# advance both "master" and "side" branches
+	# advance both "main" and "side" branches
 	git checkout side &&
 	echo five >file &&
 	git commit -a -m five &&
-	git checkout master &&
+	git checkout main &&
 	echo six >file &&
 	git commit -a -m six &&
 
@@ -75,7 +78,7 @@
 		sed -e "/HEAD$/d" \
 		    -e "s|/remotes/origin/|/heads/|" refs >../actual
 	) &&
-	# follow both master and side
+	# follow both main and side
 	git for-each-ref refs/heads >expect &&
 	test_cmp expect actual
 '
@@ -100,20 +103,20 @@
 	test_must_be_empty actual
 '
 
-test_expect_success '--single-branch while HEAD pointing at master' '
+test_expect_success '--single-branch while HEAD pointing at main' '
 	(
-		cd dir_master &&
+		cd dir_main &&
 		git fetch --force &&
 		git for-each-ref refs/remotes/origin >refs &&
 		sed -e "/HEAD$/d" \
 		    -e "s|/remotes/origin/|/heads/|" refs >../actual
 	) &&
-	# only follow master
-	git for-each-ref refs/heads/master >expect &&
+	# only follow main
+	git for-each-ref refs/heads/main >expect &&
 	# get & check latest tags
 	test_cmp expect actual &&
 	(
-		cd dir_master &&
+		cd dir_main &&
 		git fetch --tags --force &&
 		git for-each-ref refs/tags >../actual
 	) &&
@@ -122,20 +125,20 @@
 	test_line_count = 2 actual
 '
 
-test_expect_success '--single-branch while HEAD pointing at master and --no-tags' '
+test_expect_success '--single-branch while HEAD pointing at main and --no-tags' '
 	(
-		cd dir_master_no_tags &&
+		cd dir_main_no_tags &&
 		git fetch &&
 		git for-each-ref refs/remotes/origin >refs &&
 		sed -e "/HEAD$/d" \
 		    -e "s|/remotes/origin/|/heads/|" refs >../actual
 	) &&
-	# only follow master
-	git for-each-ref refs/heads/master >expect &&
+	# only follow main
+	git for-each-ref refs/heads/main >expect &&
 	test_cmp expect actual &&
 	# get tags (noop)
 	(
-		cd dir_master_no_tags &&
+		cd dir_main_no_tags &&
 		git fetch &&
 		git for-each-ref refs/tags >../actual
 	) &&
@@ -143,7 +146,7 @@
 	test_line_count = 0 actual &&
 	# get tags with --tags overrides tagOpt
 	(
-		cd dir_master_no_tags &&
+		cd dir_main_no_tags &&
 		git fetch --tags &&
 		git for-each-ref refs/tags >../actual
 	) &&
diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh
index e4e6ea4..5504b51 100755
--- a/t/t5614-clone-submodules-shallow.sh
+++ b/t/t5614-clone-submodules-shallow.sh
@@ -7,7 +7,7 @@
 pwd=$(pwd)
 
 test_expect_success 'setup' '
-	git checkout -b master &&
+	git checkout -b main &&
 	test_commit commit1 &&
 	test_commit commit2 &&
 	mkdir sub &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index d98c550..5cb4153 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -2,6 +2,9 @@
 
 test_description='git partial clone'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # create a normal "src" repo where we can later create new commits.
@@ -51,14 +54,14 @@
 	test_line_count = 1 promisorlist &&
 	git -C srv.bare rev-parse --verify HEAD >headhash &&
 	grep "$(cat headhash) HEAD" $(cat promisorlist) &&
-	grep "$(cat headhash) refs/heads/master" $(cat promisorlist)
+	grep "$(cat headhash) refs/heads/main" $(cat promisorlist)
 '
 
-# checkout master to force dynamic object fetch of blobs at HEAD.
+# checkout main to force dynamic object fetch of blobs at HEAD.
 test_expect_success 'verify checkout with dynamic object fetch' '
 	git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
 	test_line_count = 4 observed &&
-	git -C pc1 checkout master &&
+	git -C pc1 checkout main &&
 	git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
 	test_line_count = 0 observed
 '
@@ -73,8 +76,8 @@
 		git -C src add file.1.txt
 		git -C src commit -m "mod $x"
 	done &&
-	git -C src blame master -- file.1.txt >expect.blame &&
-	git -C src push -u srv master
+	git -C src blame main -- file.1.txt >expect.blame &&
+	git -C src push -u srv main
 '
 
 # (partial) fetch in the partial clone repo from the promisor remote.
@@ -83,26 +86,26 @@
 test_expect_success 'partial fetch inherits filter settings' '
 	git -C pc1 fetch origin &&
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		master..origin/master >observed &&
+		main..origin/main >observed &&
 	test_line_count = 5 observed
 '
 
 # force dynamic object fetch using diff.
-# we should only get 1 new blob (for the file in origin/master).
+# we should only get 1 new blob (for the file in origin/main).
 test_expect_success 'verify diff causes dynamic object fetch' '
-	git -C pc1 diff master..origin/master -- file.1.txt &&
+	git -C pc1 diff main..origin/main -- file.1.txt &&
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		 master..origin/master >observed &&
+		 main..origin/main >observed &&
 	test_line_count = 4 observed
 '
 
 # force full dynamic object fetch of the file's history using blame.
 # we should get the intermediate blobs for the file.
 test_expect_success 'verify blame causes dynamic object fetch' '
-	git -C pc1 blame origin/master -- file.1.txt >observed.blame &&
+	git -C pc1 blame origin/main -- file.1.txt >observed.blame &&
 	test_cmp expect.blame observed.blame &&
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		master..origin/master >observed &&
+		main..origin/main >observed &&
 	test_line_count = 0 observed
 '
 
@@ -115,7 +118,7 @@
 		git -C src add file.2.txt
 		git -C src commit -m "mod $x"
 	done &&
-	git -C src push -u srv master
+	git -C src push -u srv main
 '
 
 # Do FULL fetch by disabling inherited filter-spec using --no-filter.
@@ -123,7 +126,7 @@
 test_expect_success 'override inherited filter-spec using --no-filter' '
 	git -C pc1 fetch --no-filter origin &&
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		master..origin/master >observed &&
+		main..origin/main >observed &&
 	test_line_count = 0 observed
 '
 
@@ -136,7 +139,7 @@
 		git -C src add file.3.txt
 		git -C src commit -m "mod $x"
 	done &&
-	git -C src push -u srv master
+	git -C src push -u srv main
 '
 
 # Do a partial fetch and then try to manually fetch the missing objects.
@@ -146,7 +149,7 @@
 	git -C pc1 fetch --filter=blob:none origin &&
 
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		 master..origin/master >revs &&
+		 main..origin/main >revs &&
 	awk -f print_1.awk revs |
 	sed "s/?//" |
 	sort >observed.oids &&
@@ -155,7 +158,7 @@
 	git -C pc1 fetch-pack --stdin "file://$(pwd)/srv.bare" <observed.oids &&
 
 	git -C pc1 rev-list --quiet --objects --missing=print \
-		master..origin/master >revs &&
+		main..origin/main >revs &&
 	awk -f print_1.awk revs |
 	sed "s/?//" |
 	sort >observed.oids &&
@@ -196,7 +199,7 @@
 	echo "in dir" >src/dir/file.txt &&
 	git -C src add dir/file.txt &&
 	git -C src commit -m "file in dir" &&
-	git -C src push -u srv master &&
+	git -C src push -u srv main &&
 	SUBTREE=$(git -C src rev-parse HEAD:dir) &&
 
 	rm -rf dst &&
@@ -204,7 +207,7 @@
 	git -C dst fsck &&
 
 	# Make sure we only have commits, and all trees and blobs are missing.
-	git -C dst rev-list --missing=allow-any --objects master \
+	git -C dst rev-list --missing=allow-any --objects main \
 		>fetched_objects &&
 	awk -f print_1.awk fetched_objects |
 	xargs -n1 git -C dst cat-file -t >fetched_types &&
@@ -221,7 +224,7 @@
 	git -C dst fsck &&
 
 	# Auto-fetch all remaining trees and blobs with --missing=error
-	git -C dst rev-list --missing=error --objects master >fetched_objects &&
+	git -C dst rev-list --missing=error --objects main >fetched_objects &&
 	test_line_count = 70 fetched_objects &&
 
 	awk -f print_1.awk fetched_objects |
@@ -347,7 +350,7 @@
 test_expect_success 'partial clone with sparse filter succeeds' '
 	rm -rf dst.git &&
 	git clone --no-local --bare \
-		  --filter=sparse:oid=master:only-one \
+		  --filter=sparse:oid=main:only-one \
 		  sparse-src dst.git &&
 	(
 		cd dst.git &&
@@ -360,11 +363,11 @@
 test_expect_success 'partial clone with unresolvable sparse filter fails cleanly' '
 	rm -rf dst.git &&
 	test_must_fail git clone --no-local --bare \
-				 --filter=sparse:oid=master:no-such-name \
+				 --filter=sparse:oid=main:no-such-name \
 				 sparse-src dst.git 2>err &&
-	test_i18ngrep "unable to access sparse blob in .master:no-such-name" err &&
+	test_i18ngrep "unable to access sparse blob in .main:no-such-name" err &&
 	test_must_fail git clone --no-local --bare \
-				 --filter=sparse:oid=master \
+				 --filter=sparse:oid=main \
 				 sparse-src dst.git 2>err &&
 	test_i18ngrep "unable to parse sparse filter data in" err
 '
@@ -419,7 +422,7 @@
 	# promisor remote other than for the big tree (because it needs to
 	# resolve the delta).
 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
-		fetch "file://$(pwd)/server" master &&
+		fetch "file://$(pwd)/server" main &&
 
 	# Verify the assumption that the client needed to fetch the delta base
 	# to resolve the delta.
@@ -438,7 +441,7 @@
 	# promisor remote other than for the big blob (because it needs to
 	# resolve the delta).
 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
-		fetch "file://$(pwd)/server" master &&
+		fetch "file://$(pwd)/server" main &&
 
 	# Verify that protocol version 2 was used.
 	grep "fetch< version 2" trace &&
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index 1a041df..e2dbb4e 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -2,12 +2,15 @@
 
 test_description='Test cloning repos with submodules using remote-tracking branches'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 pwd=$(pwd)
 
 test_expect_success 'setup' '
-	git checkout -b master &&
+	git checkout -b main &&
 	test_commit commit1 &&
 	mkdir sub &&
 	(
@@ -39,7 +42,7 @@
 	git clone --recurse-submodules --remote-submodules "file://$pwd/." super_clone &&
 	(
 		cd super_clone/sub &&
-		git diff --exit-code remotes/origin/master
+		git diff --exit-code remotes/origin/main
 	)
 '
 
@@ -57,7 +60,7 @@
 	git clone --recurse-submodules --single-branch "file://$pwd/." super_clone &&
 	(
 		cd super_clone/sub &&
-		git rev-parse --verify origin/master &&
+		git rev-parse --verify origin/main &&
 		test_must_fail git rev-parse --verify origin/other
 	)
 '
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index 022901b..468bd3e 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -8,6 +8,9 @@
 GIT_TEST_PROTOCOL_VERSION=0
 export GIT_TEST_PROTOCOL_VERSION
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Test protocol v1 with 'git://' transport
@@ -41,7 +44,7 @@
 	GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
 		fetch 2>log &&
 
-	git -C daemon_child log -1 --format=%s origin/master >actual &&
+	git -C daemon_child log -1 --format=%s origin/main >actual &&
 	git -C "$daemon_parent" log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -69,7 +72,7 @@
 	test_commit -C daemon_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET=1 git -C daemon_child -c protocol.version=1 \
 		push origin HEAD:client_branch 2>log &&
 
@@ -110,7 +113,7 @@
 	GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
 		fetch 2>log &&
 
-	git -C file_child log -1 --format=%s origin/master >actual &&
+	git -C file_child log -1 --format=%s origin/main >actual &&
 	git -C file_parent log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -134,7 +137,7 @@
 	test_commit -C file_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET=1 git -C file_child -c protocol.version=1 \
 		push origin HEAD:client_branch 2>log &&
 
@@ -188,7 +191,7 @@
 		fetch 2>log &&
 	expect_ssh git-upload-pack &&
 
-	git -C ssh_child log -1 --format=%s origin/master >actual &&
+	git -C ssh_child log -1 --format=%s origin/main >actual &&
 	git -C ssh_parent log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -213,7 +216,7 @@
 	test_commit -C ssh_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET=1 git -C ssh_child -c protocol.version=1 \
 		push origin HEAD:client_branch 2>log &&
 	expect_ssh git-receive-pack &&
@@ -257,7 +260,7 @@
 	GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
 		fetch 2>log &&
 
-	git -C http_child log -1 --format=%s origin/master >actual &&
+	git -C http_child log -1 --format=%s origin/main >actual &&
 	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -281,7 +284,7 @@
 	test_commit -C http_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET=1 git -C http_child -c protocol.version=1 \
 		push origin HEAD:client_branch && #2>log &&
 
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index a1f5fdc..509f379 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -2,6 +2,9 @@
 
 test_description='test protocol v2 server commands'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'test capability advertisement' '
@@ -12,7 +15,7 @@
 	cat >expect <<-EOF &&
 	version 2
 	agent=git/$(git version | cut -d" " -f3)
-	ls-refs
+	ls-refs=unborn
 	fetch=shallow
 	server-option
 	object-format=$(test_oid algo)
@@ -83,9 +86,9 @@
 #
 test_expect_success 'setup some refs and tags' '
 	test_commit one &&
-	git branch dev master &&
+	git branch dev main &&
 	test_commit two &&
-	git symbolic-ref refs/heads/release refs/heads/master &&
+	git symbolic-ref refs/heads/release refs/heads/main &&
 	git tag -a -m "annotated tag" annotated-tag
 '
 
@@ -99,7 +102,7 @@
 	cat >expect <<-EOF &&
 	$(git rev-parse HEAD) HEAD
 	$(git rev-parse refs/heads/dev) refs/heads/dev
-	$(git rev-parse refs/heads/master) refs/heads/master
+	$(git rev-parse refs/heads/main) refs/heads/main
 	$(git rev-parse refs/heads/release) refs/heads/release
 	$(git rev-parse refs/tags/annotated-tag) refs/tags/annotated-tag
 	$(git rev-parse refs/tags/one) refs/tags/one
@@ -117,13 +120,13 @@
 	command=ls-refs
 	object-format=$(test_oid algo)
 	0001
-	ref-prefix refs/heads/master
+	ref-prefix refs/heads/main
 	ref-prefix refs/tags/one
 	0000
 	EOF
 
 	cat >expect <<-EOF &&
-	$(git rev-parse refs/heads/master) refs/heads/master
+	$(git rev-parse refs/heads/main) refs/heads/main
 	$(git rev-parse refs/tags/one) refs/tags/one
 	0000
 	EOF
@@ -144,7 +147,7 @@
 
 	cat >expect <<-EOF &&
 	$(git rev-parse refs/heads/dev) refs/heads/dev
-	$(git rev-parse refs/heads/master) refs/heads/master
+	$(git rev-parse refs/heads/main) refs/heads/main
 	$(git rev-parse refs/heads/release) refs/heads/release
 	0000
 	EOF
@@ -188,8 +191,8 @@
 
 	cat >expect <<-EOF &&
 	$(git rev-parse refs/heads/dev) refs/heads/dev
-	$(git rev-parse refs/heads/master) refs/heads/master
-	$(git rev-parse refs/heads/release) refs/heads/release symref-target:refs/heads/master
+	$(git rev-parse refs/heads/main) refs/heads/main
+	$(git rev-parse refs/heads/release) refs/heads/release symref-target:refs/heads/main
 	0000
 	EOF
 
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 7d5b179..2e1243c 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -4,6 +4,9 @@
 
 TEST_NO_CREATE_REPO=1
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Test protocol v2 with 'git://' transport
@@ -36,10 +39,10 @@
 	test_when_finished "rm -f log" &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
-		ls-remote "$GIT_DAEMON_URL/parent" master >actual &&
+		ls-remote "$GIT_DAEMON_URL/parent" main >actual &&
 
 	cat >expect <<-EOF &&
-	$(git -C "$daemon_parent" rev-parse refs/heads/master)$(printf "\t")refs/heads/master
+	$(git -C "$daemon_parent" rev-parse refs/heads/main)$(printf "\t")refs/heads/main
 	EOF
 
 	test_cmp expect actual
@@ -69,7 +72,7 @@
 	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
 		fetch &&
 
-	git -C daemon_child log -1 --format=%s origin/master >actual &&
+	git -C daemon_child log -1 --format=%s origin/main >actual &&
 	git -C "$daemon_parent" log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -118,7 +121,7 @@
 	test_commit -C daemon_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_child -c protocol.version=2 \
 		push origin HEAD:client_branch &&
 
@@ -158,10 +161,10 @@
 	test_when_finished "rm -f log" &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
-		ls-remote "file://$(pwd)/file_parent" master >actual &&
+		ls-remote "file://$(pwd)/file_parent" main >actual &&
 
 	cat >expect <<-EOF &&
-	$(git -C file_parent rev-parse refs/heads/master)$(printf "\t")refs/heads/master
+	$(git -C file_parent rev-parse refs/heads/main)$(printf "\t")refs/heads/main
 	EOF
 
 	test_cmp expect actual
@@ -171,10 +174,10 @@
 	test_when_finished "rm -f log" &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -c protocol.version=2 \
-		ls-remote -o hello -o world "file://$(pwd)/file_parent" master >actual &&
+		ls-remote -o hello -o world "file://$(pwd)/file_parent" main >actual &&
 
 	cat >expect <<-EOF &&
-	$(git -C file_parent rev-parse refs/heads/master)$(printf "\t")refs/heads/master
+	$(git -C file_parent rev-parse refs/heads/main)$(printf "\t")refs/heads/main
 	EOF
 
 	test_cmp expect actual &&
@@ -184,7 +187,7 @@
 
 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 &&
+		ls-remote -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
 	test_i18ngrep "see protocol.version in" err &&
 	test_i18ngrep "server options require protocol version 2 or later" err
@@ -209,6 +212,31 @@
 	grep "ref-prefix refs/tags/" log
 '
 
+test_expect_success 'clone of empty repo propagates name of default branch' '
+	test_when_finished "rm -rf file_empty_parent file_empty_child" &&
+
+	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+	git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+
+	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+	git -c init.defaultBranch=main -c protocol.version=2 \
+		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
+	grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+'
+
+test_expect_success '...but not if explicitly forbidden by config' '
+	test_when_finished "rm -rf file_empty_parent file_empty_child" &&
+
+	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+	git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+	test_config -C file_empty_parent lsrefs.unborn ignore &&
+
+	GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+	git -c init.defaultBranch=main -c protocol.version=2 \
+		clone "file://$(pwd)/file_empty_parent" file_empty_child &&
+	! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+'
+
 test_expect_success 'fetch with file:// using protocol v2' '
 	test_when_finished "rm -f log" &&
 
@@ -217,7 +245,7 @@
 	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
 		fetch origin &&
 
-	git -C file_child log -1 --format=%s origin/master >actual &&
+	git -C file_child log -1 --format=%s origin/main >actual &&
 	git -C file_parent log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -232,13 +260,13 @@
 	git -C file_parent branch unwanted-branch three &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
-		fetch origin master &&
+		fetch origin main &&
 
-	git -C file_child log -1 --format=%s origin/master >actual &&
+	git -C file_child log -1 --format=%s origin/main >actual &&
 	git -C file_parent log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
-	grep "refs/heads/master" log &&
+	grep "refs/heads/main" log &&
 	! grep "refs/heads/unwanted-branch" log
 '
 
@@ -248,9 +276,9 @@
 	test_commit -C file_parent four &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
-		fetch -o hello -o world origin master &&
+		fetch -o hello -o world origin main &&
 
-	git -C file_child log -1 --format=%s origin/master >actual &&
+	git -C file_child log -1 --format=%s origin/main >actual &&
 	git -C file_parent log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -264,7 +292,7 @@
 	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 &&
+		fetch -o hello -o world "file://$(pwd)/file_parent" main 2>err &&
 
 	test_i18ngrep "see protocol.version in" err &&
 	test_i18ngrep "server options require protocol version 2 or later" err
@@ -325,7 +353,7 @@
 	grep "version 2" trace &&
 
 	# Ensure that the old version of the file is missing
-	git -C client rev-list --quiet --objects --missing=print master \
+	git -C client rev-list --quiet --objects --missing=print main \
 		>observed.oids &&
 	grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
 
@@ -350,7 +378,7 @@
 	SERVER="file://$(pwd)/server" &&
 
 	GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \
-		fetch --filter=blob:none "$SERVER" master:refs/heads/other &&
+		fetch --filter=blob:none "$SERVER" main:refs/heads/other &&
 	grep "version 2" trace &&
 
 	# Ensure that the old version of the file is missing
@@ -395,7 +423,7 @@
 	command=fetch
 	object-format=$(test_oid algo)
 	0001
-	want $(git -C server rev-parse master)
+	want $(git -C server rev-parse main)
 	filter blob:none
 	0000
 	EOF
@@ -539,7 +567,7 @@
 	test_commit -C server four &&
 
 	# Sanity check that only "three" is downloaded
-	git -C client log --pretty=tformat:%s master >actual &&
+	git -C client log --pretty=tformat:%s main >actual &&
 	echo three >expected &&
 	test_cmp expected actual &&
 
@@ -548,7 +576,7 @@
 	# Ensure that protocol v2 is used
 	grep "fetch< version 2" trace &&
 
-	git -C client log --pretty=tformat:%s origin/master >actual &&
+	git -C client log --pretty=tformat:%s origin/main >actual &&
 	cat >expected <<-\EOF &&
 	four
 	three
@@ -670,7 +698,7 @@
 	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
 		fetch &&
 
-	git -C http_child log -1 --format=%s origin/master >actual &&
+	git -C http_child log -1 --format=%s origin/main >actual &&
 	git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" log -1 --format=%s >expect &&
 	test_cmp expect actual &&
 
@@ -698,11 +726,11 @@
 	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" one &&
 	test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" two &&
 	git -C "$HTTPD_DOCUMENT_ROOT_PATH/nsrepo" \
-		update-ref refs/namespaces/ns/refs/heads/master one &&
+		update-ref refs/namespaces/ns/refs/heads/main one &&
 
 	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
 		fetch "$HTTPD_URL/smart_namespace/nsrepo" \
-		refs/heads/master:refs/heads/theirs &&
+		refs/heads/main:refs/heads/theirs &&
 
 	# Server responded using protocol v2
 	grep "fetch< version 2" log &&
@@ -733,7 +761,7 @@
 	test_commit -C http_child three &&
 
 	# Push to another branch, as the target repository has the
-	# master branch checked out and we cannot push into it.
+	# main branch checked out and we cannot push into it.
 	GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
 		push origin HEAD:client_branch &&
 
@@ -847,11 +875,33 @@
 	test -f hfound &&
 	test -f h2found &&
 
-	# Ensure that there are exactly 6 files (3 .pack and 3 .idx).
-	ls http_child/.git/objects/pack/* >filelist &&
+	# Ensure that there are exactly 3 packfiles with associated .idx
+	ls http_child/.git/objects/pack/*.pack \
+	    http_child/.git/objects/pack/*.idx >filelist &&
 	test_line_count = 6 filelist
 '
 
+test_expect_success 'packfile URIs with fetch instead of clone' '
+	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+	rm -rf "$P" http_child log &&
+
+	git init "$P" &&
+	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+	echo my-blob >"$P/my-blob" &&
+	git -C "$P" add my-blob &&
+	git -C "$P" commit -m x &&
+
+	configure_exclusion "$P" my-blob >h &&
+
+	git init http_child &&
+
+	GIT_TEST_SIDEBAND_ALL=1 \
+	git -C http_child -c protocol.version=2 \
+		-c fetch.uriprotocols=http,https \
+		fetch "$HTTPD_URL/smart/http_parent"
+'
+
 test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
 	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
 	rm -rf "$P" http_child log &&
@@ -901,8 +951,9 @@
 		-c fetch.uriprotocols=http,https \
 		clone "$HTTPD_URL/smart/http_parent" http_child &&
 
-	# Ensure that there are exactly 4 files (2 .pack and 2 .idx).
-	ls http_child/.git/objects/pack/* >filelist &&
+	# Ensure that there are exactly 2 packfiles with associated .idx
+	ls http_child/.git/objects/pack/*.pack \
+	    http_child/.git/objects/pack/*.idx >filelist &&
 	test_line_count = 4 filelist
 '
 
@@ -936,6 +987,54 @@
 	test_i18ngrep "invalid author/committer line - missing email" error
 '
 
+test_expect_success 'packfile-uri with transfer.fsckobjects succeeds when .gitmodules is separate from tree' '
+	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+	rm -rf "$P" http_child &&
+
+	git init "$P" &&
+	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+	echo "[submodule libfoo]" >"$P/.gitmodules" &&
+	echo "path = include/foo" >>"$P/.gitmodules" &&
+	echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
+	git -C "$P" add .gitmodules &&
+	git -C "$P" commit -m x &&
+
+	configure_exclusion "$P" .gitmodules >h &&
+
+	sane_unset GIT_TEST_SIDEBAND_ALL &&
+	git -c protocol.version=2 -c transfer.fsckobjects=1 \
+		-c fetch.uriprotocols=http,https \
+		clone "$HTTPD_URL/smart/http_parent" http_child &&
+
+	# Ensure that there are exactly 2 packfiles with associated .idx
+	ls http_child/.git/objects/pack/*.pack \
+	    http_child/.git/objects/pack/*.idx >filelist &&
+	test_line_count = 4 filelist
+'
+
+test_expect_success 'packfile-uri with transfer.fsckobjects fails when .gitmodules separate from tree is invalid' '
+	P="$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
+	rm -rf "$P" http_child err &&
+
+	git init "$P" &&
+	git -C "$P" config "uploadpack.allowsidebandall" "true" &&
+
+	echo "[submodule \"..\"]" >"$P/.gitmodules" &&
+	echo "path = include/foo" >>"$P/.gitmodules" &&
+	echo "url = git://example.com/git/lib.git" >>"$P/.gitmodules" &&
+	git -C "$P" add .gitmodules &&
+	git -C "$P" commit -m x &&
+
+	configure_exclusion "$P" .gitmodules >h &&
+
+	sane_unset GIT_TEST_SIDEBAND_ALL &&
+	test_must_fail git -c protocol.version=2 -c transfer.fsckobjects=1 \
+		-c fetch.uriprotocols=http,https \
+		clone "$HTTPD_URL/smart/http_parent" http_child 2>err &&
+	test_i18ngrep "disallowed submodule name" err
+'
+
 # DO NOT add non-httpd-specific tests here, because the last part of this
 # test script is only executed when httpd is available and enabled.
 
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index eab9669..e9e4716 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -2,6 +2,9 @@
 
 test_description='upload-pack ref-in-want'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 get_actual_refs () {
@@ -16,7 +19,8 @@
 	test-tool pkt-line unpack-sideband <out >o.pack &&
 	git index-pack o.pack &&
 	git verify-pack -v o.idx >objs &&
-	grep commit objs | cut -d" " -f1 | sort >actual_commits
+	sed -n -e 's/\([0-9a-f][0-9a-f]*\) commit .*/\1/p' objs >objs.sed &&
+	sort >actual_commits <objs.sed
 }
 
 check_output () {
diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh
index 0f04b6c..d386076 100755
--- a/t/t5801-remote-helpers.sh
+++ b/t/t5801-remote-helpers.sh
@@ -5,6 +5,9 @@
 
 test_description='Test remote-helper import and export commands'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
@@ -71,18 +74,18 @@
 	(cd local &&
 	 git fetch
 	) &&
-	compare_refs server master local refs/remotes/origin/master &&
+	compare_refs server main local refs/remotes/origin/main &&
 	compare_refs server new local refs/remotes/origin/new
 '
 
 test_expect_success 'push when remote has extra refs' '
 	(cd local &&
-	 git reset --hard origin/master &&
+	 git reset --hard origin/main &&
 	 echo content >>file &&
 	 git commit -a -m six &&
 	 git push
 	) &&
-	compare_refs local master server master
+	compare_refs local main server main
 '
 
 test_expect_success 'push new branch by name' '
@@ -174,7 +177,7 @@
 
 test_expect_success 'push all with existing object' '
 	(cd local &&
-	git branch dup2 master &&
+	git branch dup2 main &&
 	git push origin --all
 	) &&
 	compare_refs local dup2 server dup2
@@ -182,7 +185,7 @@
 
 test_expect_success 'push ref with existing object' '
 	(cd local &&
-	git branch dup master &&
+	git branch dup main &&
 	git push origin dup
 	) &&
 	compare_refs local dup server dup
@@ -190,7 +193,7 @@
 
 test_expect_success GPG 'push signed tag' '
 	(cd local &&
-	git checkout master &&
+	git checkout main &&
 	git tag -s -m signed-tag signed-tag &&
 	git push origin signed-tag
 	) &&
@@ -200,7 +203,7 @@
 
 test_expect_success GPG 'push signed tag with signed-tags capability' '
 	(cd local &&
-	git checkout master &&
+	git checkout main &&
 	git tag -s -m signed-tag signed-tag-2 &&
 	GIT_REMOTE_TESTGIT_SIGNED_TAGS=1 git push origin signed-tag-2
 	) &&
@@ -209,7 +212,7 @@
 
 test_expect_success 'push update refs' '
 	(cd local &&
-	git checkout -b update master &&
+	git checkout -b update main &&
 	echo update >>file &&
 	git commit -a -m update &&
 	git push origin update &&
@@ -260,7 +263,7 @@
 test_expect_success 'proper failure checks for pushing' '
 	test_when_finished "rm -rf local/git.marks local/testgit.marks" &&
 	(cd local &&
-	git checkout -b crash master &&
+	git checkout -b crash main &&
 	echo crash >>file &&
 	git commit -a -m crash &&
 	test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git push --all &&
@@ -272,7 +275,7 @@
 
 test_expect_success 'push messages' '
 	(cd local &&
-	git checkout -b new_branch master &&
+	git checkout -b new_branch main &&
 	echo new >>file &&
 	git commit -a -m new &&
 	git push origin new_branch &&
@@ -286,7 +289,7 @@
 
 test_expect_success 'fetch HEAD' '
 	(cd server &&
-	git checkout master &&
+	git checkout main &&
 	echo more >>file &&
 	git commit -a -m more
 	) &&
@@ -298,7 +301,7 @@
 
 test_expect_success 'fetch url' '
 	(cd server &&
-	git checkout master &&
+	git checkout main &&
 	echo more >>file &&
 	git commit -a -m more
 	) &&
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index fc4d55d..12def7b 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -2,6 +2,9 @@
 
 test_description='miscellaneous rev-list tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -85,7 +88,7 @@
 '
 
 test_expect_success 'symleft flag bit is propagated down from tag' '
-	git log --format="%m %s" --left-right v1.0...master >actual &&
+	git log --format="%m %s" --left-right v1.0...main >actual &&
 	cat >expect <<-\EOF &&
 	< another
 	< that
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
index 7504ba4..90d93f7 100755
--- a/t/t6001-rev-list-graft.sh
+++ b/t/t6001-rev-list-graft.sh
@@ -2,6 +2,9 @@
 
 test_description='Revision traversal vs grafts and path limiter'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -20,7 +23,7 @@
 	git commit -a -m "Third in one history." &&
 	A2=$(git rev-parse --verify HEAD) &&
 
-	rm -f .git/refs/heads/master .git/index &&
+	rm -f .git/refs/heads/main .git/index &&
 
 	echo >fileA fileA again &&
 	echo >subdir/fileB fileB again &&
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
index 3e8c42e..cd4f420 100755
--- a/t/t6004-rev-list-path-optim.sh
+++ b/t/t6004-rev-list-path-optim.sh
@@ -4,7 +4,7 @@
 
    d/z1
    b0                             b1
-   o------------------------*----o master
+   o------------------------*----o main
   /                        /
  o---------o----o----o----o side
  a0        c0   c1   a1   c2
@@ -13,6 +13,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -55,18 +58,18 @@
 	git add c &&
 	test_tick &&
 	git commit -m "Side makes yet another irrelevant commit" &&
-	git checkout master &&
+	git checkout main &&
 	echo Another >b &&
 	echo Munged >d/z &&
 	git add b d/z &&
 	test_tick &&
-	git commit -m "Master touches b" &&
-	git tag master_b0 &&
+	git commit -m "Main touches b" &&
+	git tag main_b0 &&
 	git merge side &&
 	echo Touched >b &&
 	git add b &&
 	test_tick &&
-	git commit -m "Master touches b again"
+	git commit -m "Main touches b again"
 '
 
 test_expect_success 'path optimization 2' '
@@ -76,13 +79,13 @@
 '
 
 test_expect_success 'pathspec with leading path' '
-	git rev-parse master^ master_b0 side_c0 initial >expected &&
+	git rev-parse main^ main_b0 side_c0 initial >expected &&
 	git rev-list HEAD -- d >actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'pathspec with glob (1)' '
-	git rev-parse master^ master_b0 side_c0 initial >expected &&
+	git rev-parse main^ main_b0 side_c0 initial >expected &&
 	git rev-list HEAD -- "d/*" >actual &&
 	test_cmp expected actual
 '
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 99a1eaf..35a2f62 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -5,6 +5,9 @@
 
 test_description='git rev-list --pretty=format test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
@@ -53,7 +56,7 @@
 test_format () {
 	cat >expect.$1
 	test_expect_${3:-success} "format $1" "
-		git rev-list --pretty=format:'$2' master >output.$1 &&
+		git rev-list --pretty=format:'$2' main >output.$1 &&
 		test_cmp expect.$1 output.$1
 	"
 }
@@ -184,13 +187,13 @@
 	<RED>foo<GREEN>bar<BLUE>baz<RESET>xyzzy
 	EOF
 	format="%Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy" &&
-	git rev-list --color --format="$format" -1 master >actual.raw &&
+	git rev-list --color --format="$format" -1 main >actual.raw &&
 	test_decode_color <actual.raw >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success '%S is not a placeholder for rev-list yet' '
-	git rev-list --format="%S" -1 master | grep "%S"
+	git rev-list --format="%S" -1 main | grep "%S"
 '
 
 test_expect_success 'advanced colors' '
@@ -199,7 +202,7 @@
 	<BOLD;RED;BYELLOW>foo<RESET>
 	EOF
 	format="%C(red yellow bold)foo%C(reset)" &&
-	git rev-list --color --format="$format" -1 master >actual.raw &&
+	git rev-list --color --format="$format" -1 main >actual.raw &&
 	test_decode_color <actual.raw >actual &&
 	test_cmp expect actual
 '
@@ -406,7 +409,7 @@
 
 test_expect_success '%ad respects --date=' '
 	echo 2005-04-07 >expect.ad-short &&
-	git log -1 --date=short --pretty=tformat:%ad >output.ad-short master &&
+	git log -1 --date=short --pretty=tformat:%ad >output.ad-short main &&
 	test_cmp expect.ad-short output.ad-short
 '
 
@@ -494,8 +497,8 @@
 '
 
 test_expect_success '%gd shortens ref name' '
-	echo "master@{0}" >expect.gd-short &&
-	git log -g -1 --format=%gd refs/heads/master >actual.gd-short &&
+	echo "main@{0}" >expect.gd-short &&
+	git log -g -1 --format=%gd refs/heads/main >actual.gd-short &&
 	test_cmp expect.gd-short actual.gd-short
 '
 
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index 8bf5ae2..aebe4b6 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -2,6 +2,9 @@
 
 test_description='test git rev-list --cherry-pick -- file'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # A---B---D---F
@@ -31,7 +34,7 @@
 	test_tick &&
 	git commit -m "E" &&
 	git tag E &&
-	git checkout master &&
+	git checkout main &&
 	git checkout branch foo &&
 	test_tick &&
 	git commit -m "B" &&
@@ -233,7 +236,7 @@
 	test_tick &&
 	git commit -m "independent, too" foo &&
 	test -z "$(git rev-list --left-right --cherry-pick \
-		HEAD...master -- foo)"
+		HEAD...main -- foo)"
 '
 
 cat >expect <<EOF
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index c4af9ca..3153a0d 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -5,6 +5,9 @@
 
 test_description='git rev-list involving submodules that this repo has'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -36,7 +39,7 @@
 '
 
 test_expect_success "Ilari's test" '
-	git rev-list --objects super master ^super^
+	git rev-list --objects super main ^super^
 '
 
 test_done
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
index 916d969..63fa7c8 100755
--- a/t/t6009-rev-list-parent.sh
+++ b/t/t6009-rev-list-parent.sh
@@ -2,6 +2,9 @@
 
 test_description='ancestor culling and limiting by parent number'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_revlist () {
@@ -45,7 +48,7 @@
 	test_commit seven &&
 	git checkout -b yetanotherbranch four &&
 	test_commit eight &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git merge --allow-unrelated-histories -m normalmerge newroot &&
 	git tag normalmerge &&
@@ -56,7 +59,7 @@
 	test_tick &&
 	git merge -m tetrapus sidebranch anotherbranch yetanotherbranch &&
 	git tag tetrapus &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'rev-list roots' '
@@ -123,7 +126,7 @@
 		roots="$roots root$i" ||
 		return
 	done &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git merge -m dodecapus $roots &&
 	git tag dodecapus &&
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index fd202fc..4f7fa8b 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -2,6 +2,9 @@
 
 test_description='merge simplification'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 note () {
@@ -43,7 +46,7 @@
 	git add side &&
 	test_tick && git commit -m "Side root" &&
 	note J &&
-	git checkout master &&
+	git checkout main &&
 
 	echo "Hello" >file &&
 	echo "second" >lost &&
@@ -65,7 +68,7 @@
 	note D &&
 
 	test_tick &&
-	test_must_fail git merge -m "merge" master &&
+	test_must_fail git merge -m "merge" main &&
 	>lost && git commit -a -m "merge" &&
 	note E &&
 
@@ -74,7 +77,7 @@
 	test_tick && git commit -m "Irrelevant change" &&
 	note F &&
 
-	git checkout master &&
+	git checkout main &&
 	echo "Yet another" >elif &&
 	git add elif &&
 	test_tick && git commit -m "Another irrelevant change" &&
@@ -87,7 +90,7 @@
 	test_tick && git commit -a -m "Final change" &&
 	note I &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick && git merge --allow-unrelated-histories -m "Coolest" unrelated &&
 	note K &&
 
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
index 89458d3..39793cb 100755
--- a/t/t6013-rev-list-reverse-parents.sh
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -2,6 +2,9 @@
 
 test_description='--reverse combines with --parents'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
@@ -18,24 +21,24 @@
 	commit two &&
 	git checkout -b side HEAD^ &&
 	commit three &&
-	git checkout master &&
+	git checkout main &&
 	git merge -s ours side &&
 	commit five
 	'
 
 test_expect_success '--reverse --parents --full-history combines correctly' '
-	git rev-list --parents --full-history master -- foo |
+	git rev-list --parents --full-history main -- foo |
 		perl -e "print reverse <>" > expected &&
-	git rev-list --reverse --parents --full-history master -- foo \
+	git rev-list --reverse --parents --full-history main -- foo \
 		> actual &&
 	test_cmp expected actual
 	'
 
 test_expect_success '--boundary does too' '
-	git rev-list --boundary --parents --full-history master ^root -- foo |
+	git rev-list --boundary --parents --full-history main ^root -- foo |
 		perl -e "print reverse <>" > expected &&
 	git rev-list --boundary --reverse --parents --full-history \
-		master ^root -- foo > actual &&
+		main ^root -- foo > actual &&
 	test_cmp expected actual
 	'
 
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
index f79df8b..54b0a6f 100755
--- a/t/t6016-rev-list-graph-simplify-history.sh
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -7,6 +7,9 @@
 
 test_description='--graph and simplified history'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
@@ -20,7 +23,7 @@
 	test_commit A1 foo.txt &&
 	test_commit A2 bar.txt &&
 	test_commit A3 bar.txt &&
-	git branch -m master A &&
+	git branch -m main A &&
 
 	# 2 commits on branch B, started from A1
 	git checkout -b B A1 &&
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
index 667b375..0516251 100755
--- a/t/t6017-rev-list-stdin.sh
+++ b/t/t6017-rev-list-stdin.sh
@@ -5,6 +5,9 @@
 
 test_description='log family learns --stdin'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check () {
@@ -40,7 +43,7 @@
 		done &&
 		for i in $them
 		do
-			git checkout -b side-$i master~$i &&
+			git checkout -b side-$i main~$i &&
 			echo updated $i >file-$i &&
 			git add file-$i &&
 			test_tick &&
@@ -49,7 +52,7 @@
 	)
 '
 
-check master
+check main
 check side-1 ^side-4
 check side-1 ^side-7 --
 check side-1 ^side-7 -- file-1
@@ -66,11 +69,11 @@
 	file-2
 	EOF
 	cat >input <<-EOF &&
-	^master^
+	^main^
 	--
 	file-2
 	EOF
-	git log --pretty=tformat:%s --name-only --stdin master -- file-1 \
+	git log --pretty=tformat:%s --name-only --stdin main -- file-1 \
 		<input >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index fe2f3ce..24b34ad 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -2,6 +2,9 @@
 
 test_description='rev-list/rev-parse --glob'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 commit () {
@@ -20,22 +23,22 @@
 
 test_expect_success 'setup' '
 
-	commit master &&
-	git checkout -b subspace/one master &&
+	commit main &&
+	git checkout -b subspace/one main &&
 	commit one &&
-	git checkout -b subspace/two master &&
+	git checkout -b subspace/two main &&
 	commit two &&
-	git checkout -b subspace-x master &&
+	git checkout -b subspace-x main &&
 	commit subspace-x &&
-	git checkout -b other/three master &&
+	git checkout -b other/three main &&
 	commit three &&
-	git checkout -b someref master &&
+	git checkout -b someref main &&
 	commit some &&
-	git checkout master &&
+	git checkout main &&
 	commit topic_2 &&
-	git tag foo/bar master &&
+	git tag foo/bar main &&
 	commit topic_3 &&
-	git update-ref refs/remotes/foo/baz master &&
+	git update-ref refs/remotes/foo/baz main &&
 	commit topic_4 &&
 	git update-ref refs/remotes/upstream/one subspace/one &&
 	git update-ref refs/remotes/upstream/two subspace/two &&
@@ -83,7 +86,7 @@
 
 test_expect_failure 'rev-parse is not confused by option-like glob' '
 
-	compare rev-parse "master" "--glob --symbolic master"
+	compare rev-parse "main" "--glob --symbolic main"
 
 '
 
@@ -111,15 +114,15 @@
 
 '
 
-test_expect_success 'rev-parse --glob=heads/someref/* master' '
+test_expect_success 'rev-parse --glob=heads/someref/* main' '
 
-	compare rev-parse "master" "--glob=heads/someref/* master"
+	compare rev-parse "main" "--glob=heads/someref/* main"
 
 '
 
 test_expect_success 'rev-parse --glob=heads/*' '
 
-	compare rev-parse "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+	compare rev-parse "main other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
 
 '
 
@@ -136,7 +139,7 @@
 '
 
 test_expect_success 'rev-parse --exclude with --branches' '
-	compare rev-parse "--exclude=*/* --branches" "master someref subspace-x"
+	compare rev-parse "--exclude=*/* --branches" "main someref subspace-x"
 '
 
 test_expect_success 'rev-parse --exclude with --all' '
@@ -221,7 +224,7 @@
 
 test_expect_success 'rev-list not confused by option-like --glob arg' '
 
-	compare rev-list "master" "--glob -0 master"
+	compare rev-list "main" "--glob -0 main"
 
 '
 
@@ -269,13 +272,13 @@
 
 test_expect_success 'rev-list --branches' '
 
-	compare rev-list "master subspace-x someref other/three subspace/one subspace/two" "--branches"
+	compare rev-list "main subspace-x someref other/three subspace/one subspace/two" "--branches"
 
 '
 
-test_expect_success 'rev-list --glob=heads/someref/* master' '
+test_expect_success 'rev-list --glob=heads/someref/* main' '
 
-	compare rev-list "master" "--glob=heads/someref/* master"
+	compare rev-list "main" "--glob=heads/someref/* main"
 
 '
 
@@ -287,7 +290,7 @@
 
 test_expect_success 'rev-list --glob=heads/*' '
 
-	compare rev-list "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+	compare rev-list "main other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
 
 '
 
@@ -310,7 +313,7 @@
 '
 
 test_expect_success 'rev-list --exclude with --branches' '
-	compare rev-list "--exclude=*/* --branches" "master someref subspace-x"
+	compare rev-list "--exclude=*/* --branches" "main someref subspace-x"
 '
 
 test_expect_success 'rev-list --exclude with --all' '
@@ -354,13 +357,13 @@
 
 	compare shortlog "subspace/one subspace/two" --branches=subspace &&
 	compare shortlog \
-	  "master subspace-x someref other/three subspace/one subspace/two" \
+	  "main subspace-x someref other/three subspace/one subspace/two" \
 	  --branches &&
-	compare shortlog master "--glob=heads/someref/* master" &&
+	compare shortlog main "--glob=heads/someref/* main" &&
 	compare shortlog "subspace/one subspace/two other/three" \
 	  "--glob=heads/subspace/* --glob=heads/other/*" &&
 	compare shortlog \
-	  "master other/three someref subspace-x subspace/one subspace/two" \
+	  "main other/three someref subspace-x subspace/one subspace/two" \
 	  "--glob=heads/*" &&
 	compare shortlog foo/bar --tags=foo &&
 	compare shortlog "foo/bar qux/one qux/two qux/x" --tags &&
@@ -371,14 +374,14 @@
 test_expect_failure 'shortlog accepts --glob as detached option' '
 
 	compare shortlog \
-	  "master other/three someref subspace-x subspace/one subspace/two" \
+	  "main other/three someref subspace-x subspace/one subspace/two" \
 	  "--glob heads/*"
 
 '
 
 test_expect_failure 'shortlog --glob is not confused by option-like argument' '
 
-	compare shortlog master "--glob -e master"
+	compare shortlog main "--glob -e main"
 
 '
 
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index 353f843..20adbec 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -21,6 +21,9 @@
 #  --ancestry-path G..M -- G.t == L
 #  --ancestry-path --simplify-merges G^..M -- G.t == G L
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_merge () {
@@ -128,15 +131,15 @@
 	(cd criss-cross &&
 	 git init &&
 	 test_commit A &&
-	 git checkout -b xb master &&
+	 git checkout -b xb main &&
 	 test_commit B &&
-	 git checkout -b xc master &&
+	 git checkout -b xc main &&
 	 test_commit C &&
 	 git checkout -b xbc xb -- &&
 	 git merge xc &&
 	 git checkout -b xcb xc -- &&
 	 git merge xb &&
-	 git checkout master)
+	 git checkout main)
 '
 
 # no commits in bc descend from cb
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
new file mode 100755
index 0000000..881f72f
--- /dev/null
+++ b/t/t6020-bundle-misc.sh
@@ -0,0 +1,463 @@
+#!/bin/sh
+#
+# Copyright (c) 2021 Jiang Xin
+#
+
+test_description='Test git-bundle'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
+
+# Create a commit or tag and set the variable with the object ID.
+test_commit_setvar () {
+	notick=
+	signoff=
+	indir=
+	merge=
+	tag=
+	var=
+
+	while test $# != 0
+	do
+		case "$1" in
+		--merge)
+			merge=t
+			;;
+		--tag)
+			tag=t
+			;;
+		--notick)
+			notick=t
+			;;
+		--signoff)
+			signoff="$1"
+			;;
+		-C)
+			shift
+			indir="$1"
+			;;
+		-*)
+			echo >&2 "error: unknown option $1"
+			return 1
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+	if test $# -lt 2
+	then
+		echo >&2 "error: test_commit_setvar must have at least 2 arguments"
+		return 1
+	fi
+	var=$1
+	shift
+	indir=${indir:+"$indir"/}
+	if test -z "$notick"
+	then
+		test_tick
+	fi &&
+	if test -n "$merge"
+	then
+		git ${indir:+ -C "$indir"} merge --no-edit --no-ff \
+			${2:+-m "$2"} "$1" &&
+		oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD)
+	elif test -n "$tag"
+	then
+		git ${indir:+ -C "$indir"} tag -m "$1" "$1" "${2:-HEAD}" &&
+		oid=$(git ${indir:+ -C "$indir"} rev-parse "$1")
+	else
+		file=${2:-"$1.t"} &&
+		echo "${3-$1}" >"$indir$file" &&
+		git ${indir:+ -C "$indir"} add "$file" &&
+		git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
+		oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD)
+	fi &&
+	eval $var=$oid
+}
+
+# Format the output of git commands to make a user-friendly and stable
+# text.  We can easily prepare the expect text without having to worry
+# about future changes of the commit ID and spaces of the output.
+make_user_friendly_and_stable_output () {
+	sed \
+		-e "s/${A%${A#???????}}[0-9a-f]*/<COMMIT-A>/g" \
+		-e "s/${B%${B#???????}}[0-9a-f]*/<COMMIT-B>/g" \
+		-e "s/${C%${C#???????}}[0-9a-f]*/<COMMIT-C>/g" \
+		-e "s/${D%${D#???????}}[0-9a-f]*/<COMMIT-D>/g" \
+		-e "s/${E%${E#???????}}[0-9a-f]*/<COMMIT-E>/g" \
+		-e "s/${F%${F#???????}}[0-9a-f]*/<COMMIT-F>/g" \
+		-e "s/${G%${G#???????}}[0-9a-f]*/<COMMIT-G>/g" \
+		-e "s/${H%${H#???????}}[0-9a-f]*/<COMMIT-H>/g" \
+		-e "s/${I%${I#???????}}[0-9a-f]*/<COMMIT-I>/g" \
+		-e "s/${J%${J#???????}}[0-9a-f]*/<COMMIT-J>/g" \
+		-e "s/${K%${K#???????}}[0-9a-f]*/<COMMIT-K>/g" \
+		-e "s/${L%${L#???????}}[0-9a-f]*/<COMMIT-L>/g" \
+		-e "s/${M%${M#???????}}[0-9a-f]*/<COMMIT-M>/g" \
+		-e "s/${N%${N#???????}}[0-9a-f]*/<COMMIT-N>/g" \
+		-e "s/${O%${O#???????}}[0-9a-f]*/<COMMIT-O>/g" \
+		-e "s/${P%${P#???????}}[0-9a-f]*/<COMMIT-P>/g" \
+		-e "s/${TAG1%${TAG1#???????}}[0-9a-f]*/<TAG-1>/g" \
+		-e "s/${TAG2%${TAG2#???????}}[0-9a-f]*/<TAG-2>/g" \
+		-e "s/${TAG3%${TAG3#???????}}[0-9a-f]*/<TAG-3>/g" \
+		-e "s/ *\$//"
+}
+
+#            (C)   (D, pull/1/head, topic/1)
+#             o --- o
+#            /       \                              (L)
+#           /         \        o (H, topic/2)             (M, tag:v2)
+#          /    (F)    \      /                                 (N, tag:v3)
+#         /      o --------- o (G, pull/2/head)      o --- o --- o (release)
+#        /      /        \    \                      /       \
+#  o --- o --- o -------- o -- o ------------------ o ------- o --- o (main)
+# (A)   (B)  (E, tag:v1) (I)  (J)                  (K)       (O)   (P)
+#
+test_expect_success 'setup' '
+	# Try to make a stable fixed width for abbreviated commit ID,
+	# this fixed-width oid will be replaced with "<OID>".
+	git config core.abbrev 7 &&
+
+	# branch main: commit A & B
+	test_commit_setvar A "Commit A" main.txt &&
+	test_commit_setvar B "Commit B" main.txt &&
+
+	# branch topic/1: commit C & D, refs/pull/1/head
+	git checkout -b topic/1 &&
+	test_commit_setvar C "Commit C" topic-1.txt &&
+	test_commit_setvar D "Commit D" topic-1.txt &&
+	git update-ref refs/pull/1/head HEAD &&
+
+	# branch topic/1: commit E, tag v1
+	git checkout main &&
+	test_commit_setvar E "Commit E" main.txt &&
+	test_commit_setvar --tag TAG1 v1 &&
+
+	# branch topic/2: commit F & G, refs/pull/2/head
+	git checkout -b topic/2 &&
+	test_commit_setvar F "Commit F" topic-2.txt &&
+	test_commit_setvar G "Commit G" topic-2.txt &&
+	git update-ref refs/pull/2/head HEAD &&
+	test_commit_setvar H "Commit H" topic-2.txt &&
+
+	# branch main: merge commit I & J
+	git checkout main &&
+	test_commit_setvar --merge I topic/1 "Merge commit I" &&
+	test_commit_setvar --merge J refs/pull/2/head "Merge commit J" &&
+
+	# branch main: commit K
+	git checkout main &&
+	test_commit_setvar K "Commit K" main.txt &&
+
+	# branch release:
+	git checkout -b release &&
+	test_commit_setvar L "Commit L" release.txt &&
+	test_commit_setvar M "Commit M" release.txt &&
+	test_commit_setvar --tag TAG2 v2 &&
+	test_commit_setvar N "Commit N" release.txt &&
+	test_commit_setvar --tag TAG3 v3 &&
+
+	# branch main: merge commit O, commit P
+	git checkout main &&
+	test_commit_setvar --merge O tags/v2 "Merge commit O" &&
+	test_commit_setvar P "Commit P" main.txt
+'
+
+test_expect_success 'create bundle from special rev: main^!' '
+	git bundle create special-rev.bdl "main^!" &&
+
+	git bundle list-heads special-rev.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	<COMMIT-P> refs/heads/main
+	EOF
+	test_cmp expect actual &&
+
+	git bundle verify special-rev.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	The bundle contains this ref:
+	<COMMIT-P> refs/heads/main
+	The bundle requires this ref:
+	<COMMIT-O>
+	EOF
+	test_cmp expect actual &&
+
+	test_bundle_object_count special-rev.bdl 3
+'
+
+test_expect_success 'create bundle with --max-count option' '
+	git bundle create max-count.bdl --max-count 1 \
+		main \
+		"^release" \
+		refs/tags/v1 \
+		refs/pull/1/head \
+		refs/pull/2/head &&
+
+	git bundle verify max-count.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	The bundle contains these 2 refs:
+	<COMMIT-P> refs/heads/main
+	<TAG-1> refs/tags/v1
+	The bundle requires this ref:
+	<COMMIT-O>
+	EOF
+	test_cmp expect actual &&
+
+	test_bundle_object_count max-count.bdl 4
+'
+
+test_expect_success 'create bundle with --since option' '
+	git log -1 --pretty="%ad" $M >actual &&
+	cat >expect <<-\EOF &&
+	Thu Apr 7 15:26:13 2005 -0700
+	EOF
+	test_cmp expect actual &&
+
+	git bundle create since.bdl \
+		--since "Thu Apr 7 15:27:00 2005 -0700" \
+		--all &&
+
+	git bundle verify since.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	The bundle contains these 5 refs:
+	<COMMIT-P> refs/heads/main
+	<COMMIT-N> refs/heads/release
+	<TAG-2> refs/tags/v2
+	<TAG-3> refs/tags/v3
+	<COMMIT-P> HEAD
+	The bundle requires these 2 refs:
+	<COMMIT-M>
+	<COMMIT-K>
+	EOF
+	test_cmp expect actual &&
+
+	test_bundle_object_count --thin since.bdl 13
+'
+
+test_expect_success 'create bundle 1 - no prerequisites' '
+	# create bundle from args
+	git bundle create 1.bdl topic/1 topic/2 &&
+
+	# create bundle from stdin
+	cat >input <<-\EOF &&
+	topic/1
+	topic/2
+	EOF
+	git bundle create stdin-1.bdl --stdin <input &&
+
+	cat >expect <<-\EOF &&
+	The bundle contains these 2 refs:
+	<COMMIT-D> refs/heads/topic/1
+	<COMMIT-H> refs/heads/topic/2
+	The bundle records a complete history.
+	EOF
+
+	# verify bundle, which has no prerequisites
+	git bundle verify 1.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	git bundle verify stdin-1.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	test_bundle_object_count       1.bdl 24 &&
+	test_bundle_object_count stdin-1.bdl 24
+'
+
+test_expect_success 'create bundle 2 - has prerequisites' '
+	# create bundle from args
+	git bundle create 2.bdl \
+		--ignore-missing \
+		^topic/deleted \
+		^$D \
+		^topic/2 \
+		release &&
+
+	# create bundle from stdin
+	# input has a non-exist reference: "topic/deleted"
+	cat >input <<-EOF &&
+	^topic/deleted
+	^$D
+	^topic/2
+	EOF
+	git bundle create stdin-2.bdl \
+		--ignore-missing \
+		--stdin \
+		release <input &&
+
+	cat >expect <<-\EOF &&
+	The bundle contains this ref:
+	<COMMIT-N> refs/heads/release
+	The bundle requires these 3 refs:
+	<COMMIT-D>
+	<COMMIT-E>
+	<COMMIT-G>
+	EOF
+
+	git bundle verify 2.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	git bundle verify stdin-2.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	test_bundle_object_count       2.bdl 16 &&
+	test_bundle_object_count stdin-2.bdl 16
+'
+
+test_expect_success 'fail to verify bundle without prerequisites' '
+	git init --bare test1.git &&
+
+	cat >expect <<-\EOF &&
+	error: Repository lacks these prerequisite commits:
+	error: <COMMIT-D>
+	error: <COMMIT-E>
+	error: <COMMIT-G>
+	EOF
+
+	test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'create bundle 3 - two refs, same object' '
+	# create bundle from args
+	git bundle create --version=3 3.bdl \
+		^release \
+		^topic/1 \
+		^topic/2 \
+		main \
+		HEAD &&
+
+	# create bundle from stdin
+	cat >input <<-\EOF &&
+	^release
+	^topic/1
+	^topic/2
+	EOF
+	git bundle create --version=3 stdin-3.bdl \
+		--stdin \
+		main HEAD <input &&
+
+	cat >expect <<-\EOF &&
+	The bundle contains these 2 refs:
+	<COMMIT-P> refs/heads/main
+	<COMMIT-P> HEAD
+	The bundle requires these 2 refs:
+	<COMMIT-M>
+	<COMMIT-K>
+	EOF
+
+	git bundle verify 3.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	git bundle verify stdin-3.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	test_bundle_object_count       3.bdl 4 &&
+	test_bundle_object_count stdin-3.bdl 4
+'
+
+test_expect_success 'create bundle 4 - with tags' '
+	# create bundle from args
+	git bundle create 4.bdl \
+		^main \
+		^release \
+		^topic/1 \
+		^topic/2 \
+		--all &&
+
+	# create bundle from stdin
+	cat >input <<-\EOF &&
+	^main
+	^release
+	^topic/1
+	^topic/2
+	EOF
+	git bundle create stdin-4.bdl \
+		--ignore-missing \
+		--stdin \
+		--all <input &&
+
+	cat >expect <<-\EOF &&
+	The bundle contains these 3 refs:
+	<TAG-1> refs/tags/v1
+	<TAG-2> refs/tags/v2
+	<TAG-3> refs/tags/v3
+	The bundle records a complete history.
+	EOF
+
+	git bundle verify 4.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	git bundle verify stdin-4.bdl |
+		make_user_friendly_and_stable_output >actual &&
+	test_cmp expect actual &&
+
+	test_bundle_object_count       4.bdl 3 &&
+	test_bundle_object_count stdin-4.bdl 3
+'
+
+test_expect_success 'clone from bundle' '
+	git clone --mirror 1.bdl mirror.git &&
+	git -C mirror.git show-ref |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	<COMMIT-D> refs/heads/topic/1
+	<COMMIT-H> refs/heads/topic/2
+	EOF
+	test_cmp expect actual &&
+
+	git -C mirror.git fetch ../2.bdl "+refs/*:refs/*" &&
+	git -C mirror.git show-ref |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	<COMMIT-N> refs/heads/release
+	<COMMIT-D> refs/heads/topic/1
+	<COMMIT-H> refs/heads/topic/2
+	EOF
+	test_cmp expect actual &&
+
+	git -C mirror.git fetch ../3.bdl "+refs/*:refs/*" &&
+	git -C mirror.git show-ref |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	<COMMIT-P> refs/heads/main
+	<COMMIT-N> refs/heads/release
+	<COMMIT-D> refs/heads/topic/1
+	<COMMIT-H> refs/heads/topic/2
+	EOF
+	test_cmp expect actual &&
+
+	git -C mirror.git fetch ../4.bdl "+refs/*:refs/*" &&
+	git -C mirror.git show-ref |
+		make_user_friendly_and_stable_output >actual &&
+	cat >expect <<-\EOF &&
+	<COMMIT-P> refs/heads/main
+	<COMMIT-N> refs/heads/release
+	<COMMIT-D> refs/heads/topic/1
+	<COMMIT-H> refs/heads/topic/2
+	<TAG-1> refs/tags/v1
+	<TAG-2> refs/tags/v2
+	<TAG-3> refs/tags/v3
+	EOF
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 52614ee..0ba5a91 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -6,6 +6,9 @@
 
 exec </dev/null
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 add_line_into_file()
@@ -89,9 +92,9 @@
 	grep bar ".git/BISECT_NAMES"
 '
 
-test_expect_success 'bisect reset: back in the master branch' '
+test_expect_success 'bisect reset: back in the main branch' '
 	git bisect reset &&
-	echo "* master" > branch.expect &&
+	echo "* main" > branch.expect &&
 	git branch > branch.output &&
 	cmp branch.expect branch.output
 '
@@ -102,7 +105,7 @@
 	git bisect good $HASH1 &&
 	git bisect bad $HASH3 &&
 	git bisect reset &&
-	echo "  master" > branch.expect &&
+	echo "  main" > branch.expect &&
 	echo "* other" >> branch.expect &&
 	git branch > branch.output &&
 	cmp branch.expect branch.output
@@ -348,7 +351,7 @@
 
 test_expect_success 'bisect starting with a detached HEAD' '
 	git bisect reset &&
-	git checkout master^ &&
+	git checkout main^ &&
 	HEAD=$(git rev-parse --verify HEAD) &&
 	git bisect start &&
 	test $HEAD = $(cat .git/BISECT_START) &&
@@ -575,9 +578,9 @@
 	test "$para3" = "$PARA_HASH3"
 '
 
-test_expect_success 'erroring out when using bad path parameters' '
+test_expect_success 'erroring out when using bad path arguments' '
 	test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
-	test_i18ngrep "bad path parameters" error.txt
+	test_i18ngrep "bad path arguments" error.txt
 '
 
 test_expect_success 'test bisection on bare repo - --no-checkout specified' '
@@ -716,7 +719,7 @@
 test_expect_success 'bisect: demonstrate identification of damage boundary' "
 	git bisect reset &&
 	git checkout broken &&
-	git bisect start broken master --no-checkout &&
+	git bisect start broken main --no-checkout &&
 	test_must_fail git bisect run \"\$SHELL_PATH\" -c '
 		GOOD=\$(git for-each-ref \"--format=%(objectname)\" refs/bisect/good-*) &&
 		git rev-list --objects BISECT_HEAD --not \$GOOD >tmp.\$\$ &&
@@ -826,7 +829,7 @@
 	test_must_fail git bisect terms 1 2 &&
 	test_must_fail git bisect terms 2>actual &&
 	echo "error: no terms defined" >expected &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'bisect terms shows good/bad after start' '
@@ -900,7 +903,7 @@
 	Your current terms are two for the old state
 	and one for the new state.
 	EOF
-	test_i18ncmp expected actual &&
+	test_cmp expected actual &&
 	git bisect terms --term-bad >actual &&
 	echo one >expected &&
 	test_cmp expected actual &&
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
index 50a934e..a313849 100755
--- a/t/t6040-tracking-info.sh
+++ b/t/t6040-tracking-info.sh
@@ -2,6 +2,9 @@
 
 test_description='remote tracking stats'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 advance () {
@@ -34,7 +37,7 @@
 		git branch -d brokenbase &&
 		git checkout -b b6 origin
 	) &&
-	git checkout -b follower --track master &&
+	git checkout -b follower --track main &&
 	advance h
 '
 
@@ -54,16 +57,16 @@
 		git branch -v
 	) |
 	sed -n -e "$t6040_script" >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
-b1 [origin/master: ahead 1, behind 1] d
-b2 [origin/master: ahead 1, behind 1] d
-b3 [origin/master: behind 1] b
-b4 [origin/master: ahead 2] f
+b1 [origin/main: ahead 1, behind 1] d
+b2 [origin/main: ahead 1, behind 1] d
+b3 [origin/main: behind 1] b
+b4 [origin/main: ahead 2] f
 b5 [brokenbase: gone] g
-b6 [origin/master] c
+b6 [origin/main] c
 EOF
 
 test_expect_success 'branch -vv' '
@@ -72,7 +75,7 @@
 		git branch -vv
 	) |
 	sed -n -e "$t6040_script" >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'checkout (diverged from upstream)' '
@@ -83,7 +86,7 @@
 '
 
 test_expect_success 'checkout with local tracked branch' '
-	git checkout master &&
+	git checkout main &&
 	git checkout follower >actual &&
 	test_i18ngrep "is ahead of" actual
 '
@@ -100,7 +103,7 @@
 	(
 		cd test && git checkout b6
 	) >actual &&
-	test_i18ngrep "Your branch is up to date with .origin/master" actual
+	test_i18ngrep "Your branch is up to date with .origin/main" actual
 '
 
 test_expect_success 'status (diverged from upstream)' '
@@ -130,11 +133,11 @@
 		# reports nothing to commit
 		test_must_fail git commit --dry-run
 	) >actual &&
-	test_i18ngrep "Your branch is up to date with .origin/master" actual
+	test_i18ngrep "Your branch is up to date with .origin/main" actual
 '
 
 cat >expect <<\EOF
-## b1...origin/master [ahead 1, behind 1]
+## b1...origin/main [ahead 1, behind 1]
 EOF
 
 test_expect_success 'status -s -b (diverged from upstream)' '
@@ -143,11 +146,11 @@
 		git checkout b1 >/dev/null &&
 		git status -s -b | head -1
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
-## b1...origin/master [different]
+## b1...origin/main [different]
 EOF
 
 test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '
@@ -156,11 +159,11 @@
 		git checkout b1 >/dev/null &&
 		git status -s -b --no-ahead-behind | head -1
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
-## b1...origin/master [different]
+## b1...origin/main [different]
 EOF
 
 test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstream)' '
@@ -169,12 +172,12 @@
 		git checkout b1 >/dev/null &&
 		git -c status.aheadbehind=false status -s -b | head -1
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
 On branch b1
-Your branch and 'origin/master' have diverged,
+Your branch and 'origin/main' have diverged,
 and have 1 and 1 different commits each, respectively.
 EOF
 
@@ -184,7 +187,7 @@
 		git checkout b1 >/dev/null &&
 		git status --long -b | head -3
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'status --long --branch' '
@@ -193,12 +196,12 @@
 		git checkout b1 >/dev/null &&
 		git -c status.aheadbehind=true status --long -b | head -3
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
 On branch b1
-Your branch and 'origin/master' refer to different commits.
+Your branch and 'origin/main' refer to different commits.
 EOF
 
 test_expect_success 'status --long --branch --no-ahead-behind' '
@@ -207,7 +210,7 @@
 		git checkout b1 >/dev/null &&
 		git status --long -b --no-ahead-behind | head -2
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'status.aheadbehind=false status --long --branch' '
@@ -216,7 +219,7 @@
 		git checkout b1 >/dev/null &&
 		git -c status.aheadbehind=false status --long -b | head -2
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -229,11 +232,11 @@
 		git checkout b5 >/dev/null &&
 		git status -s -b | head -1
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<\EOF
-## b6...origin/master
+## b6...origin/main
 EOF
 
 test_expect_success 'status -s -b (up-to-date with upstream)' '
@@ -242,11 +245,11 @@
 		git checkout b6 >/dev/null &&
 		git status -s -b | head -1
 	) >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'fail to track lightweight tags' '
-	git checkout master &&
+	git checkout main &&
 	git tag light &&
 	test_must_fail git branch --track lighttrack light >actual &&
 	test_i18ngrep ! "set up to track" actual &&
@@ -254,7 +257,7 @@
 '
 
 test_expect_success 'fail to track annotated tags' '
-	git checkout master &&
+	git checkout main &&
 	git tag -m heavy heavy &&
 	test_must_fail git branch --track heavytrack heavy >actual &&
 	test_i18ngrep ! "set up to track" actual &&
@@ -262,17 +265,17 @@
 '
 
 test_expect_success '--set-upstream-to does not change branch' '
-	git branch from-master master &&
-	git branch --set-upstream-to master from-master &&
-	git branch from-topic_2 master &&
+	git branch from-main main &&
+	git branch --set-upstream-to main from-main &&
+	git branch from-topic_2 main &&
 	test_must_fail git config branch.from-topic_2.merge > actual &&
 	git rev-list from-topic_2 &&
 	git update-ref refs/heads/from-topic_2 from-topic_2^ &&
 	git rev-parse from-topic_2 >expect2 &&
-	git branch --set-upstream-to master from-topic_2 &&
-	git config branch.from-master.merge > actual &&
+	git branch --set-upstream-to main from-topic_2 &&
+	git config branch.from-main.merge > actual &&
 	git rev-parse from-topic_2 >actual2 &&
-	grep -q "^refs/heads/master$" actual &&
+	grep -q "^refs/heads/main$" actual &&
 	cmp expect2 actual2
 '
 
@@ -280,11 +283,11 @@
 	git checkout follower &&
 	git checkout from-topic_2 &&
 	git config branch.from-topic_2.merge > expect2 &&
-	git branch --set-upstream-to @{-1} from-master &&
-	git config branch.from-master.merge > actual &&
+	git branch --set-upstream-to @{-1} from-main &&
+	git config branch.from-main.merge > actual &&
 	git config branch.from-topic_2.merge > actual2 &&
-	git branch --set-upstream-to follower from-master &&
-	git config branch.from-master.merge > expect &&
+	git branch --set-upstream-to follower from-main &&
+	git config branch.from-main.merge > expect &&
 	test_cmp expect2 actual2 &&
 	test_cmp expect actual
 '
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index c80dc10..e33d512 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -4,6 +4,9 @@
 #
 test_description='Tests replace refs functionality'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -129,13 +132,13 @@
 EOF
 
 test_expect_success 'tag replaced commit' '
-     git mktag <tag.sig >.git/refs/tags/mytag 2>message
+     git mktag <tag.sig >.git/refs/tags/mytag
 '
 
 test_expect_success '"git fsck" works' '
-     git fsck master >fsck_master.out &&
-     test_i18ngrep "dangling commit $R" fsck_master.out &&
-     test_i18ngrep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_master.out &&
+     git fsck main >fsck_main.out &&
+     test_i18ngrep "dangling commit $R" fsck_main.out &&
+     test_i18ngrep "dangling tag $(git show-ref -s refs/tags/mytag)" fsck_main.out &&
      test -z "$(git fsck)"
 '
 
@@ -218,7 +221,7 @@
      git cherry-pick $HASH6 &&
      PARA6=$(git rev-parse --verify HEAD) &&
      git replace $HASH6 $PARA6 &&
-     git checkout master &&
+     git checkout main &&
      cur=$(git rev-parse --verify HEAD) &&
      test "$cur" = "$HASH7" &&
      git log --pretty=oneline | grep $PARA2 &&
@@ -461,7 +464,7 @@
 	git commit -m "hello: 2 more lines from a test branch" &&
 	HASH9=$(git rev-parse --verify HEAD) &&
 	git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
-	git checkout master &&
+	git checkout main &&
 	git merge -s ours test_tag &&
 	HASH10=$(git rev-parse --verify HEAD) &&
 	git cat-file commit $HASH10 | grep "^mergetag object"
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 7531262..78b5851 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -5,6 +5,9 @@
 
 test_description='Test git rev-parse with different parent options'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_cmp_rev_output () {
@@ -18,7 +21,7 @@
 	test_commit second &&
 	git checkout --orphan tmp &&
 	test_commit start2 &&
-	git checkout master &&
+	git checkout main &&
 	git merge -m next --allow-unrelated-histories start2 &&
 	test_commit final &&
 
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 656ac7f..13c1da5 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='operations that cull histories in unusual ways'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -10,7 +13,7 @@
 	git checkout -b side HEAD^ &&
 	test_commit D &&
 	test_commit E &&
-	git merge master
+	git merge main
 '
 
 test_expect_success 'rev-list --first-parent --boundary' '
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
index 4244638..e07b607 100755
--- a/t/t6111-rev-list-treesame.sh
+++ b/t/t6111-rev-list-treesame.sh
@@ -13,6 +13,9 @@
 
 test_description='TREESAME and limiting'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 note () {
@@ -33,13 +36,13 @@
 	git checkout other-branch &&
 	test_commit "Added other" other "Hello" C &&
 
-	git checkout master &&
+	git checkout main &&
 	test_merge D other-branch &&
 
 	git checkout third-branch &&
 	test_commit "Third file" third "Nothing" E &&
 
-	git checkout master &&
+	git checkout main &&
 	test_commit "file=Blah" file "Blah" F &&
 
 	test_tick && git merge --no-commit third-branch &&
@@ -56,7 +59,7 @@
 
 	test_tick && git revert I && note J &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick && git merge --no-ff fiddler-branch &&
 	note K &&
 
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index de0e5a5..31457d1 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -2,6 +2,9 @@
 
 test_description='git rev-list using object filtering'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Test the blob:none filter.
@@ -223,7 +226,7 @@
 	sort >expected &&
 
 	git -C r3 rev-list --quiet --objects --filter-print-omitted \
-		--filter=sparse:oid=master:pattern HEAD >revs &&
+		--filter=sparse:oid=main:pattern HEAD >revs &&
 	awk -f print_1.awk revs |
 	sed "s/~//" |
 	sort >observed &&
@@ -436,7 +439,7 @@
 
 test_expect_success 'combine:... with more than two sub-filters' '
 	git -C r3 rev-list --objects \
-		--filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern \
+		--filter=combine:tree:3+blob:limit=40+sparse:oid=main:pattern \
 		HEAD >actual &&
 
 	expect_has HEAD "" &&
@@ -454,7 +457,7 @@
 	cp actual expect &&
 
 	git -C r3 rev-list --objects \
-		--filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern1%2brenamed%25 \
+		--filter=combine:tree:3+blob:limit=40+sparse:oid=main:pattern1%2brenamed%25 \
 		HEAD >actual &&
 	test_cmp expect actual &&
 
@@ -464,23 +467,23 @@
 	test_when_finished "rm -f trace1" &&
 	GIT_TRACE=$(pwd)/trace1 git -C r3 rev-list --objects \
 		--filter=tree:3 --filter=blob:limit=40 \
-		--filter=sparse:oid="master:p;at%ter+n" \
+		--filter=sparse:oid="main:p;at%ter+n" \
 		HEAD >actual &&
 
 	test_cmp expect actual &&
-	grep "Add to combine filter-spec: sparse:oid=master:p%3bat%25ter%2bn" \
+	grep "Add to combine filter-spec: sparse:oid=main:p%3bat%25ter%2bn" \
 		trace1 &&
 
 	# Repeat the above test, but this time, the characters to encode are in
 	# the LHS of the combined filter.
 	test_when_finished "rm -f trace2" &&
 	GIT_TRACE=$(pwd)/trace2 git -C r3 rev-list --objects \
-		--filter=sparse:oid=master:^~pattern \
+		--filter=sparse:oid=main:^~pattern \
 		--filter=tree:3 --filter=blob:limit=40 \
 		HEAD >actual &&
 
 	test_cmp expect actual &&
-	grep "Add to combine filter-spec: sparse:oid=master:%5e%7epattern" \
+	grep "Add to combine filter-spec: sparse:oid=main:%5e%7epattern" \
 		trace2
 '
 
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 2b551e6..3f88994 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list combining bitmaps and filters'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
 
 test_expect_success 'set up bitmapped repo' '
 	# one commit will have bitmaps, the other will not
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
new file mode 100755
index 0000000..b4aef32
--- /dev/null
+++ b/t/t6115-rev-list-du.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='basic tests of rev-list --disk-usage'
+. ./test-lib.sh
+
+# we want a mix of reachable and unreachable, as well as
+# objects in the bitmapped pack and some outside of it
+test_expect_success 'set up repository' '
+	test_commit --no-tag one &&
+	test_commit --no-tag two &&
+	git repack -adb &&
+	git reset --hard HEAD^ &&
+	test_commit --no-tag three &&
+	test_commit --no-tag four &&
+	git reset --hard HEAD^
+'
+
+# We don't want to hardcode sizes, because they depend on the exact details of
+# packing, zlib, etc. We'll assume that the regular rev-list and cat-file
+# machinery works and compare the --disk-usage output to that.
+disk_usage_slow () {
+	git rev-list --no-object-names "$@" |
+	git cat-file --batch-check="%(objectsize:disk)" |
+	perl -lne '$total += $_; END { print $total}'
+}
+
+# check behavior with given rev-list options; note that
+# whitespace is not preserved in args
+check_du () {
+	args=$*
+
+	test_expect_success "generate expected size ($args)" "
+		disk_usage_slow $args >expect
+	"
+
+	test_expect_success "rev-list --disk-usage without bitmaps ($args)" "
+		git rev-list --disk-usage $args >actual &&
+		test_cmp expect actual
+	"
+
+	test_expect_success "rev-list --disk-usage with bitmaps ($args)" "
+		git rev-list --disk-usage --use-bitmap-index $args >actual &&
+		test_cmp expect actual
+	"
+}
+
+check_du HEAD
+check_du --objects HEAD
+check_du --objects HEAD^..HEAD
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index f822d5d..e89b674 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -11,6 +11,9 @@
 #
 # First parent of a merge commit is on the same line, second parent below.
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_describe () {
@@ -102,7 +105,7 @@
 check_describe B --tags HEAD^^2^
 check_describe e --tags HEAD^^^
 
-check_describe heads/master --all HEAD
+check_describe heads/main --all HEAD
 check_describe tags/c-* --all HEAD^
 check_describe tags/e --all HEAD^^^
 
@@ -133,7 +136,7 @@
 EOF
 check_describe A-* HEAD
 test_expect_success 'warning was displayed for Q' '
-	test_i18ncmp err.expect err.actual
+	test_cmp err.expect err.actual
 '
 test_expect_success 'misnamed annotated tag forces long output' '
 	description=$(git describe --no-long Q^0) &&
@@ -403,15 +406,15 @@
 	i=1 &&
 	while test $i -lt 8000
 	do
-		echo "commit refs/heads/master
+		echo "commit refs/heads/main
 committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
 data <<EOF
 commit #$i
 EOF"
-		test $i = 1 && echo "from refs/heads/master^0"
+		test $i = 1 && echo "from refs/heads/main^0"
 		i=$(($i + 1))
 	done | git fast-import &&
-	git checkout master &&
+	git checkout main &&
 	git tag far-far-away HEAD^ &&
 	echo "HEAD~4000 tags/far-far-away~3999" >expect &&
 	git name-rev HEAD~4000 >actual &&
@@ -442,7 +445,7 @@
 '
 
 test_expect_success 'name-rev a rev shortly after epoch' '
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 
 	git checkout --orphan no-timestamp-underflow &&
 	# Any date closer to epoch than the CUTOFF_DATE_SLOP constant
@@ -456,7 +459,7 @@
 	test_cmp expect actual
 '
 
-# A--------------master
+# A--------------main
 #  \            /
 #   \----------M2
 #    \        /
@@ -487,10 +490,10 @@
 		git checkout $A &&
 		git merge --no-ff HEAD@{1} && # M2
 
-		git checkout master &&
+		git checkout main &&
 		git merge --no-ff HEAD@{1} &&
 
-		echo "$B master^2^2~1^2" >expect &&
+		echo "$B main^2^2~1^2" >expect &&
 		git name-rev $B >actual &&
 
 		test_cmp expect actual
@@ -516,7 +519,7 @@
 		git checkout --orphan branch && rm file &&
 		echo B > file2 && git add file2 && git commit -m B &&
 		git tag B -a -m B &&
-		git merge --no-ff --allow-unrelated-histories master -m x &&
+		git merge --no-ff --allow-unrelated-histories main -m x &&
 
 		check_describe "A-3-*" HEAD
 	)
@@ -542,7 +545,7 @@
 		echo o >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:01" git commit -m o &&
 		echo B >> file2 && git add file2 && GIT_COMMITTER_DATE="2020-01-01 15:02" git commit -m B &&
 		git tag B -a -m B &&
-		git merge --no-ff --allow-unrelated-histories master -m x &&
+		git merge --no-ff --allow-unrelated-histories main -m x &&
 
 		check_describe "B-3-*" HEAD
 	)
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index c670668..0f1cb49 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -21,7 +21,7 @@
 test_expect_success 'error message for path inside submodule' '
 	echo a >sub/a &&
 	test_must_fail git add sub/a 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'error message for path inside submodule from within submodule' '
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index f3e66ea..44f55d9 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -5,6 +5,9 @@
 
 test_description='fmt-merge-msg test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -81,7 +84,7 @@
 test_expect_success 'message for merging local branch' '
 	echo "Merge branch ${apos}left${apos}" >expected &&
 
-	git checkout master &&
+	git checkout main &&
 	git fetch . left &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
@@ -89,7 +92,7 @@
 '
 
 test_expect_success GPG 'message for merging local tag signed by good key' '
-	git checkout master &&
+	git checkout main &&
 	git fetch . signed-good-tag &&
 	git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
 	grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
@@ -98,7 +101,7 @@
 '
 
 test_expect_success GPG 'message for merging local tag signed by unknown key' '
-	git checkout master &&
+	git checkout main &&
 	git fetch . signed-good-tag &&
 	GNUPGHOME=. git fmt-merge-msg <.git/FETCH_HEAD >actual 2>&1 &&
 	grep "^Merge tag ${apos}signed-good-tag${apos}" actual &&
@@ -109,7 +112,7 @@
 test_expect_success 'message for merging external branch' '
 	echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
 
-	git checkout master &&
+	git checkout main &&
 	git fetch "$(pwd)" left &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
@@ -133,7 +136,7 @@
 	test_config merge.log true &&
 	test_unconfig merge.summary &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left &&
 
@@ -142,7 +145,7 @@
 	test_unconfig merge.log &&
 	test_config merge.summary true &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left &&
 
@@ -153,7 +156,7 @@
 '
 
 test_expect_success 'setup FETCH_HEAD' '
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left
 '
@@ -281,7 +284,7 @@
 
 	test_unconfig merge.log &&
 	test_unconfig merge.summary &&
-	git checkout master &&
+	git checkout main &&
 	git fetch "$(pwd)" left &&
 	git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
 	git fmt-merge-msg --log -m "Sync with left" \
@@ -323,28 +326,28 @@
 test_expect_success 'shortlog for two branches' '
 	test_config merge.log true &&
 	test_unconfig merge.summary &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
 	test_unconfig merge.log &&
 	test_config merge.summary true &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
 
 	test_config merge.log yes &&
 	test_unconfig merge.summary &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
 
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	git fmt-merge-msg <.git/FETCH_HEAD >actual4 &&
@@ -358,7 +361,7 @@
 test_expect_success 'merge-msg -F' '
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
@@ -368,7 +371,7 @@
 test_expect_success 'merge-msg -F in subdirectory' '
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . left right &&
 	mkdir sub &&
@@ -408,7 +411,7 @@
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . tag tag-r3 &&
 
@@ -438,7 +441,7 @@
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . tag tag-r3 tag tag-l5 &&
 
@@ -468,7 +471,7 @@
 	test_unconfig merge.log &&
 	test_config merge.summary yes &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . tag tag-r3 left &&
 
@@ -495,7 +498,7 @@
 
 	test_config merge.summary yes &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick &&
 	git fetch . long &&
 
@@ -506,11 +509,11 @@
 test_expect_success 'merge-msg with "merging" an annotated tag' '
 	test_config merge.log true &&
 
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git commit --allow-empty -m "One step ahead" &&
 	git tag -a -m "An annotated one" annote HEAD &&
 
-	git checkout master &&
+	git checkout main &&
 	git fetch . annote &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
@@ -543,23 +546,23 @@
 '
 
 test_expect_success 'merge.suppressDest configuration' '
-	git checkout -B side master &&
+	git checkout -B side main &&
 	git commit --allow-empty -m "One step ahead" &&
-	git checkout master &&
+	git checkout main &&
 	git fetch . side &&
 
 	git -c merge.suppressDest="" fmt-merge-msg <.git/FETCH_HEAD >full.1 &&
 	head -n1 full.1 >actual &&
-	grep -e "Merge branch .side. into master" actual &&
+	grep -e "Merge branch .side. into main" actual &&
 
 	git -c merge.suppressDest="mast" fmt-merge-msg <.git/FETCH_HEAD >full.2 &&
 	head -n1 full.2 >actual &&
-	grep -e "Merge branch .side. into master$" actual &&
+	grep -e "Merge branch .side. into main$" actual &&
 
 	git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
 	head -n1 full.3 >actual &&
 	grep -e "Merge branch .side." actual &&
-	! grep -e " into master$" actual
+	! grep -e " into main$" actual
 '
 
 test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 810cdbb..cac7f44 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -5,6 +5,9 @@
 
 test_description='for-each-ref test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
@@ -811,53 +814,152 @@
 	EOF
 '
 
-test_expect_success '%(trailers:unfold) unfolds trailers' '
-	{
-		unfold <trailers
-		echo
-	} >expect &&
-	git for-each-ref --format="%(trailers:unfold)" refs/heads/main >actual &&
-	test_cmp expect actual &&
-	git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/main >actual &&
-	test_cmp expect actual
-'
+test_trailer_option () {
+	title=$1 option=$2
+	cat >expect
+	test_expect_success "$title" '
+		git for-each-ref --format="%($option)" refs/heads/main >actual &&
+		test_cmp expect actual &&
+		git for-each-ref --format="%(contents:$option)" refs/heads/main >actual &&
+		test_cmp expect actual
+	'
+}
 
-test_expect_success '%(trailers:only) shows only "key: value" trailers' '
-	{
-		grep -v patch.description <trailers &&
-		echo
-	} >expect &&
-	git for-each-ref --format="%(trailers:only)" refs/heads/main >actual &&
-	test_cmp expect actual &&
-	git for-each-ref --format="%(contents:trailers:only)" refs/heads/main >actual &&
-	test_cmp expect actual
-'
+test_trailer_option '%(trailers:unfold) unfolds trailers' \
+	'trailers:unfold' <<-EOF
+	$(unfold <trailers)
 
-test_expect_success '%(trailers:only) and %(trailers:unfold) work together' '
-	{
-		grep -v patch.description <trailers | unfold &&
-		echo
-	} >expect &&
-	git for-each-ref --format="%(trailers:only,unfold)" refs/heads/main >actual &&
-	test_cmp expect actual &&
-	git for-each-ref --format="%(trailers:unfold,only)" refs/heads/main >actual &&
-	test_cmp actual actual &&
-	git for-each-ref --format="%(contents:trailers:only,unfold)" refs/heads/main >actual &&
-	test_cmp expect actual &&
-	git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/main >actual &&
-	test_cmp actual actual
-'
+	EOF
 
-test_expect_success '%(trailers) rejects unknown trailers arguments' '
-	# error message cannot be checked under i18n
-	cat >expect <<-EOF &&
+test_trailer_option '%(trailers:only) shows only "key: value" trailers' \
+	'trailers:only' <<-EOF
+	$(grep -v patch.description <trailers)
+
+	EOF
+
+test_trailer_option '%(trailers:only=no,only=true) shows only "key: value" trailers' \
+	'trailers:only=no,only=true' <<-EOF
+	$(grep -v patch.description <trailers)
+
+	EOF
+
+test_trailer_option '%(trailers:only=yes) shows only "key: value" trailers' \
+	'trailers:only=yes' <<-EOF
+	$(grep -v patch.description <trailers)
+
+	EOF
+
+test_trailer_option '%(trailers:only=no) shows all trailers' \
+	'trailers:only=no' <<-EOF
+	$(cat trailers)
+
+	EOF
+
+test_trailer_option '%(trailers:only) and %(trailers:unfold) work together' \
+	'trailers:only,unfold' <<-EOF
+	$(grep -v patch.description <trailers | unfold)
+
+	EOF
+
+test_trailer_option '%(trailers:unfold) and %(trailers:only) work together' \
+	'trailers:unfold,only' <<-EOF
+	$(grep -v patch.description <trailers | unfold)
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo) shows that trailer' \
+	'trailers:key=Signed-off-by' <<-EOF
+	Signed-off-by: A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo) is case insensitive' \
+	'trailers:key=SiGned-oFf-bY' <<-EOF
+	Signed-off-by: A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo:) trailing colon also works' \
+	'trailers:key=Signed-off-by:' <<-EOF
+	Signed-off-by: A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo) multiple keys' \
+	'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF
+	Reviewed-by: A U Thor <author@example.com>
+	Signed-off-by: A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:key=nonexistent) becomes empty' \
+	'trailers:key=Shined-off-by:' <<-EOF
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \
+	'trailers:key=Acked-by' <<-EOF
+	$(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by)
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \
+	'trailers:key=Signed-Off-by,unfold' <<-EOF
+	$(unfold <trailers | grep Signed-off-by)
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \
+	'trailers:key=Signed-off-by,only=no' <<-EOF
+	Signed-off-by: A U Thor <author@example.com>
+	$(grep patch.description <trailers)
+
+	EOF
+
+test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \
+	'trailers:key=Signed-off-by,valueonly' <<-EOF
+	A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:separator) changes separator' \
+	'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+	Reviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com>
+	EOF
+
+test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \
+	'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+	Reviewed-by,A U Thor <author@example.com>
+	Signed-off-by,A U Thor <author@example.com>
+
+	EOF
+
+test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \
+	'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF
+	Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com>
+	EOF
+
+test_failing_trailer_option () {
+	title=$1 option=$2
+	cat >expect
+	test_expect_success "$title" '
+		# error message cannot be checked under i18n
+		test_must_fail git for-each-ref --format="%($option)" refs/heads/main 2>actual &&
+		test_i18ncmp expect actual &&
+		test_must_fail git for-each-ref --format="%(contents:$option)" refs/heads/main 2>actual &&
+		test_i18ncmp expect actual
+	'
+}
+
+test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \
+	'trailers:unsupported' <<-\EOF
 	fatal: unknown %(trailers) argument: unsupported
 	EOF
-	test_must_fail git for-each-ref --format="%(trailers:unsupported)" 2>actual &&
-	test_i18ncmp expect actual &&
-	test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual &&
-	test_i18ncmp expect actual
-'
+
+test_failing_trailer_option '%(trailers:key) without value is error' \
+	'trailers:key' <<-\EOF
+	fatal: expected %(trailers:key=<value>)
+	EOF
 
 test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' '
 	cat >expect <<-EOF &&
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index 809854f..40edf9d 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -20,8 +20,8 @@
 	test_when_finished "rm -f .git/$r" &&
 	echo "warning: ignoring broken ref $r" >broken-err &&
 	git for-each-ref >out 2>err &&
-	test_i18ncmp full-list out &&
-	test_i18ncmp broken-err err
+	test_cmp full-list out &&
+	test_cmp broken-err err
 '
 
 test_expect_success 'NULL_SHA1 refs are reported correctly' '
@@ -31,10 +31,10 @@
 	echo "warning: ignoring broken ref $r" >zeros-err &&
 	git for-each-ref >out 2>err &&
 	test_cmp full-list out &&
-	test_i18ncmp zeros-err err &&
+	test_cmp zeros-err err &&
 	git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
 	test_cmp brief-list brief-out &&
-	test_i18ncmp zeros-err brief-err
+	test_cmp zeros-err brief-err
 '
 
 test_expect_success 'Missing objects are reported correctly' '
@@ -43,7 +43,7 @@
 	test_when_finished "rm -f .git/$r" &&
 	echo "fatal: missing object $MISSING for $r" >missing-err &&
 	test_must_fail git for-each-ref 2>err &&
-	test_i18ncmp missing-err err &&
+	test_cmp missing-err err &&
 	(
 		cat brief-list &&
 		echo "$MISSING $r"
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 9b8c8b2..9866b1b 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -2,6 +2,9 @@
 
 test_description='test for-each-refs usage of ref-filter APIs'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 9da0838..38700d2 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='Test merge with directory/file conflicts'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'prepare repository' '
@@ -24,12 +27,12 @@
 '
 
 test_expect_success 'Merge with d/f conflicts' '
-	test_expect_code 1 git merge -m "merge msg" master
+	test_expect_code 1 git merge -m "merge msg" main
 '
 
 test_expect_success 'F/D conflict' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 	rm .git/index &&
 
 	mkdir before &&
@@ -47,7 +50,7 @@
 	git add . &&
 	git commit -m para &&
 
-	git merge master
+	git merge main
 '
 
 test_expect_success 'setup modify/delete + directory/file conflict' '
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 3f64f62..425dad9 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Merge-recursive merging renames'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 modify () {
@@ -54,9 +57,9 @@
 	git branch change &&
 	git branch change+rename &&
 
-	sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
+	sed -e "/^g /s/.*/g : main changes a line/" <A >A+ &&
 	mv A+ A &&
-	git commit -a -m "master updates A" &&
+	git commit -a -m "main updates A" &&
 
 	git checkout yellow &&
 	rm -f M &&
@@ -94,7 +97,7 @@
 	git update-index --add B &&
 	git commit -q -a -m "changed and renamed" &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'pull renaming branch into unrenaming one' \
@@ -109,7 +112,7 @@
 	sed -ne "/^g/{
 	p
 	q
-	}" B | grep master &&
+	}" B | grep main &&
 	git diff --exit-code white N
 '
 
@@ -134,7 +137,7 @@
 '
 	git reset --hard &&
 	git show-branch &&
-	test_expect_code 1 git pull . master &&
+	test_expect_code 1 git pull . main &&
 	git ls-files -u B >b.stages &&
 	test_line_count = 3 b.stages &&
 	git ls-files -s N >n.stages &&
@@ -187,7 +190,7 @@
 test_expect_success 'interference with untracked working tree file' '
 	git reset --hard &&
 	rm -f A M &&
-	git checkout -f master &&
+	git checkout -f main &&
 	git tag -f anchor &&
 	git show-branch &&
 	git pull . yellow &&
@@ -198,7 +201,7 @@
 test_expect_success 'updated working tree file should prevent the merge' '
 	git reset --hard &&
 	rm -f A M &&
-	git checkout -f master &&
+	git checkout -f main &&
 	git tag -f anchor &&
 	git show-branch &&
 	echo >>M one line addition &&
@@ -211,7 +214,7 @@
 test_expect_success 'updated working tree file should prevent the merge' '
 	git reset --hard &&
 	rm -f A M &&
-	git checkout -f master &&
+	git checkout -f main &&
 	git tag -f anchor &&
 	git show-branch &&
 	echo >>M one line addition &&
@@ -229,7 +232,7 @@
 	git tag -f anchor &&
 	git show-branch &&
 	echo >M this file should not matter &&
-	git pull . master &&
+	git pull . main &&
 	test_path_is_file M &&
 	! {
 		git ls-files -s |
@@ -764,7 +767,7 @@
 	git rm -rf df &&
 	git commit -mB &&
 
-	git checkout master &&
+	git checkout main &&
 	git rm -rf df &&
 	echo bla >df &&
 	git add -A &&
@@ -772,7 +775,7 @@
 '
 
 test_expect_success 'avoid unnecessary update, dir->(file,nothing)' '
-	git checkout -q master^0 &&
+	git checkout -q main^0 &&
 	test-tool chmtime --get -3600 df >expect &&
 	git merge side &&
 	test-tool chmtime --get df >actual &&
@@ -794,14 +797,14 @@
 	git rm -f file &&
 	git commit -m "Delete file" &&
 
-	git checkout master &&
+	git checkout main &&
 	echo bla >file &&
 	git add -A &&
 	git commit -m "Modify file"
 '
 
 test_expect_success 'avoid unnecessary update, modify/delete' '
-	git checkout -q master^0 &&
+	git checkout -q main^0 &&
 	test-tool chmtime --get -3600 file >expect &&
 	test_must_fail git merge side &&
 	test-tool chmtime --get file >actual &&
@@ -823,13 +826,13 @@
 	git add -A &&
 	git commit -m "Add file copy" &&
 
-	git checkout master &&
+	git checkout main &&
 	git mv file newfile &&
 	git commit -m "Rename file"
 '
 
 test_expect_success 'avoid unnecessary update, rename/add-dest' '
-	git checkout -q master^0 &&
+	git checkout -q main^0 &&
 	test-tool chmtime --get -3600 newfile >expect &&
 	git merge side &&
 	test-tool chmtime --get newfile >actual &&
@@ -879,15 +882,15 @@
 	git mv original_file renamed_file &&
 	git commit -mB &&
 
-	git checkout master &&
+	git checkout main &&
 	echo 8.5 >>original_file &&
 	git add original_file &&
 	git commit -mC
 '
 
-test_expect_success 'merge master into rename has correct extended markers' '
+test_expect_success 'merge main into rename has correct extended markers' '
 	git checkout rename^0 &&
-	test_must_fail git merge -s recursive master^0 &&
+	test_must_fail git merge -s recursive main^0 &&
 
 	cat >expected <<-\EOF &&
 	1
@@ -902,14 +905,14 @@
 	9
 	=======
 	8.5
-	>>>>>>> master^0:original_file
+	>>>>>>> main^0:original_file
 	EOF
 	test_cmp expected renamed_file
 '
 
-test_expect_success 'merge rename into master has correct extended markers' '
+test_expect_success 'merge rename into main has correct extended markers' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git merge -s recursive rename^0 &&
 
 	cat >expected <<-\EOF &&
@@ -945,13 +948,13 @@
 	git mv original_file renamed_file &&
 	git commit -mB &&
 
-	git checkout master &&
+	git checkout main &&
 	git rm original_file &&
 	git commit -mC
 '
 
 test_expect_success 'no spurious "refusing to lose untracked" message' '
-	git checkout master^0 &&
+	git checkout main^0 &&
 	test_must_fail git merge rename^0 2>errors.txt &&
 	! grep "refusing to lose untracked file" errors.txt
 '
diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh
index b1c3d4d..eaf48e9 100755
--- a/t/t6404-recursive-merge.sh
+++ b/t/t6404-recursive-merge.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Test merge without common ancestors'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # This scenario is based on a real-world repository of Shawn Pearce.
@@ -15,15 +18,17 @@
 export GIT_COMMITTER_DATE
 
 test_expect_success 'setup tests' '
+	GIT_TEST_COMMIT_GRAPH=0 &&
+	export GIT_TEST_COMMIT_GRAPH &&
 	echo 1 >a1 &&
 	git add a1 &&
 	GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
 
-	git checkout -b A master &&
+	git checkout -b A main &&
 	echo A >a1 &&
 	GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1 &&
 
-	git checkout -b B master &&
+	git checkout -b B main &&
 	echo B >a1 &&
 	GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1 &&
 
@@ -66,7 +71,7 @@
 '
 
 test_expect_success 'combined merge conflicts' '
-	test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git merge -m final G
+	test_must_fail git merge -m final G
 '
 
 test_expect_success 'result contains a conflict' '
@@ -82,6 +87,7 @@
 '
 
 test_expect_success 'virtual trees were processed' '
+	# TODO: fragile test, relies on ambigious merge-base resolution
 	git ls-files --stage >out &&
 
 	cat >expect <<-EOF &&
diff --git a/t/t6405-merge-symlinks.sh b/t/t6405-merge-symlinks.sh
index 6c0a90d..7435fce 100755
--- a/t/t6405-merge-symlinks.sh
+++ b/t/t6405-merge-symlinks.sh
@@ -8,6 +8,9 @@
 This tests that git merge-recursive writes merge results as plain files
 if core.symlinks is false.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -19,7 +22,7 @@
 	git branch b-file &&
 	l=$(printf file | git hash-object -t blob -w --stdin) &&
 	echo "120000 $l	symlink" | git update-index --index-info &&
-	git commit -m master &&
+	git commit -m main &&
 	git checkout b-symlink &&
 	l=$(printf file-different | git hash-object -t blob -w --stdin) &&
 	echo "120000 $l	symlink" | git update-index --index-info &&
@@ -30,28 +33,28 @@
 	git commit -m b-file
 '
 
-test_expect_success 'merge master into b-symlink, which has a different symbolic link' '
+test_expect_success 'merge main into b-symlink, which has a different symbolic link' '
 	git checkout b-symlink &&
-	test_must_fail git merge master
+	test_must_fail git merge main
 '
 
 test_expect_success 'the merge result must be a file' '
 	test_path_is_file symlink
 '
 
-test_expect_success 'merge master into b-file, which has a file instead of a symbolic link' '
+test_expect_success 'merge main into b-file, which has a file instead of a symbolic link' '
 	git reset --hard &&
 	git checkout b-file &&
-	test_must_fail git merge master
+	test_must_fail git merge main
 '
 
 test_expect_success 'the merge result must be a file' '
 	test_path_is_file symlink
 '
 
-test_expect_success 'merge b-file, which has a file instead of a symbolic link, into master' '
+test_expect_success 'merge b-file, which has a file instead of a symbolic link, into main' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 	test_must_fail git merge b-file
 '
 
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 76a55f8..d5a4ac2 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -5,6 +5,9 @@
 
 test_description='per path merge controlled by merge attribute'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -19,10 +22,10 @@
 	git branch side &&
 	for f in text binary union
 	do
-		echo Master >>$f && git add $f || return 1
+		echo Main >>$f && git add $f || return 1
 	done &&
 	test_tick &&
-	git commit -m Master &&
+	git commit -m Main &&
 
 	git checkout side &&
 	for f in text binary union
@@ -64,7 +67,7 @@
 		echo "union merge=union"
 	} >.gitattributes &&
 
-	if git merge master
+	if git merge main
 	then
 		echo Gaah, should have conflicted
 		false
@@ -87,7 +90,7 @@
 	grep "<<<<<<<" text &&
 	cmp binary-orig binary &&
 	! grep "<<<<<<<" union &&
-	grep Master union &&
+	grep Main union &&
 	grep Side union
 
 '
@@ -115,13 +118,13 @@
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
-	git merge master &&
+	git merge main &&
 
 	cmp binary union &&
 	sed -e 1,3d text >check-1 &&
-	o=$(git unpack-file master^:text) &&
+	o=$(git unpack-file main^:text) &&
 	a=$(git unpack-file side^:text) &&
-	b=$(git unpack-file master:text) &&
+	b=$(git unpack-file main:text) &&
 	sh -c "./custom-merge $o $a $b 0 text" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
@@ -136,7 +139,7 @@
 	git config --replace-all \
 	merge.custom.name "custom merge driver for testing" &&
 
-	if git merge master
+	if git merge main
 	then
 		echo "Eh? should have conflicted"
 		false
@@ -146,9 +149,9 @@
 
 	cmp binary union &&
 	sed -e 1,3d text >check-1 &&
-	o=$(git unpack-file master^:text) &&
+	o=$(git unpack-file main^:text) &&
 	a=$(git unpack-file anchor:text) &&
-	b=$(git unpack-file master:text) &&
+	b=$(git unpack-file main:text) &&
 	sh -c "./custom-merge $o $a $b 0 text" &&
 	sed -e 1,3d $a >check-2 &&
 	cmp check-1 check-2 &&
@@ -176,7 +179,7 @@
 	test_tick &&
 	(
 		cd repo1 &&
-		git fetch ../repo2 master &&
+		git fetch ../repo2 main &&
 		git merge --allow-unrelated-histories FETCH_HEAD
 	)
 '
@@ -201,7 +204,7 @@
 	# By packaging the command in test_when_finished, we get both
 	# the correctness check and the clean-up.
 	test_when_finished "kill \$(cat sleep.pid)" &&
-	git merge master
+	git merge main
 '
 
 test_done
diff --git a/t/t6407-merge-binary.sh b/t/t6407-merge-binary.sh
index 4e6c7cb..d4273f2 100755
--- a/t/t6407-merge-binary.sh
+++ b/t/t6407-merge-binary.sh
@@ -2,6 +2,9 @@
 
 test_description='ask merge-recursive to merge binary files'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -20,7 +23,7 @@
 	git ls-files -s a >E0 &&
 	git ls-files -s m | sed -e "s/ 0	/ 3	/" >E3 &&
 	test_tick &&
-	git commit -m "master adds some" &&
+	git commit -m "main adds some" &&
 
 	git checkout side &&
 	echo rezrov >>m &&
@@ -39,7 +42,7 @@
 	rm -f a* m* &&
 	git reset --hard anchor &&
 
-	if git merge -s resolve master
+	if git merge -s resolve main
 	then
 		echo Oops, should not have succeeded
 		false
@@ -54,7 +57,7 @@
 	rm -f a* m* &&
 	git reset --hard anchor &&
 
-	if git merge -s recursive master
+	if git merge -s recursive main
 	then
 		echo Oops, should not have succeeded
 		false
diff --git a/t/t6409-merge-subtree.sh b/t/t6409-merge-subtree.sh
index b8e8b6f..d406b23 100755
--- a/t/t6409-merge-subtree.sh
+++ b/t/t6409-merge-subtree.sh
@@ -2,6 +2,9 @@
 
 test_description='subtree merge strategy'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -14,10 +17,10 @@
 	echo >>hello world &&
 	git add hello &&
 	git commit -m second &&
-	git checkout master &&
+	git checkout main &&
 	for i in mundo $s; do echo $i; done >hello &&
 	git add hello &&
-	git commit -m master
+	git commit -m main
 
 '
 
@@ -36,7 +39,7 @@
 '
 
 test_expect_success 'setup topic branch' '
-	git checkout -b topic master &&
+	git checkout -b topic main &&
 	git merge -s ours --no-commit --allow-unrelated-histories sub &&
 	git read-tree --prefix=dir/ -u sub &&
 	git commit -m "initial merge of sub into topic" &&
@@ -77,8 +80,8 @@
 
 test_expect_success 'initial merge' '
 	git remote add -f gui ../git-gui &&
-	git merge -s ours --no-commit --allow-unrelated-histories gui/master &&
-	git read-tree --prefix=git-gui/ -u gui/master &&
+	git merge -s ours --no-commit --allow-unrelated-histories gui/main &&
+	git read-tree --prefix=git-gui/ -u gui/main &&
 	git commit -m "Merge git-gui as our subdirectory" &&
 	git checkout -b work &&
 	git ls-files -s >actual &&
@@ -108,10 +111,10 @@
 
 test_expect_success 'initial ambiguous subtree' '
 	cd ../git &&
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b topic_2 &&
-	git merge -s ours --no-commit gui/master &&
-	git read-tree --prefix=git-gui2/ -u gui/master &&
+	git merge -s ours --no-commit gui/main &&
+	git read-tree --prefix=git-gui2/ -u gui/main &&
 	git commit -m "Merge git-gui2 as our subdirectory" &&
 	git checkout -b work2 &&
 	git ls-files -s >actual &&
diff --git a/t/t6411-merge-filemode.sh b/t/t6411-merge-filemode.sh
index 87741ef..f54c915 100755
--- a/t/t6411-merge-filemode.sh
+++ b/t/t6411-merge-filemode.sh
@@ -1,17 +1,20 @@
 #!/bin/sh
 
 test_description='merge: handle file mode'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'set up mode change in one branch' '
 	: >file1 &&
 	git add file1 &&
 	git commit -m initial &&
-	git checkout -b a1 master &&
+	git checkout -b a1 main &&
 	: >dummy &&
 	git add dummy &&
 	git commit -m a &&
-	git checkout -b b1 master &&
+	git checkout -b b1 main &&
 	test_chmod +x file1 &&
 	git add file1 &&
 	git commit -m b1
@@ -39,12 +42,12 @@
 
 test_expect_success 'set up mode change in both branches' '
 	git reset --hard HEAD &&
-	git checkout -b a2 master &&
+	git checkout -b a2 main &&
 	: >file2 &&
 	H=$(git hash-object file2) &&
 	test_chmod +x file2 &&
 	git commit -m a2 &&
-	git checkout -b b2 master &&
+	git checkout -b b2 main &&
 	: >file2 &&
 	git add file2 &&
 	git commit -m b2 &&
@@ -76,7 +79,7 @@
 
 test_expect_success 'set up delete/modechange scenario' '
 	git reset --hard &&
-	git checkout -b deletion master &&
+	git checkout -b deletion main &&
 	git rm file1 &&
 	git commit -m deletion
 '
diff --git a/t/t6412-merge-large-rename.sh b/t/t6412-merge-large-rename.sh
index 8077738..c50d315 100755
--- a/t/t6412-merge-large-rename.sh
+++ b/t/t6412-merge-large-rename.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='merging with large rename matrix'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 count() {
@@ -30,7 +33,7 @@
 	test_expect_success "rename ($1, $2)" '
 	n='$1' &&
 	expect='$2' &&
-	git checkout -f master &&
+	git checkout -f main &&
 	test_might_fail git branch -D test$n &&
 	git reset --hard initial &&
 	for i in $(count $n); do
@@ -50,8 +53,8 @@
 	git add . &&
 	git commit -m change+rename=$n &&
 	case "$expect" in
-		ok) git merge master ;;
-		 *) test_must_fail git merge master ;;
+		ok) git merge main ;;
+		 *) test_must_fail git merge main ;;
 	esac
 	'
 }
diff --git a/t/t6413-merge-crlf.sh b/t/t6413-merge-crlf.sh
index e8d65ee..affea25 100755
--- a/t/t6413-merge-crlf.sh
+++ b/t/t6413-merge-crlf.sh
@@ -8,6 +8,9 @@
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -24,7 +27,7 @@
 	echo line from b | append_cr >file &&
 	git commit -m "add line from b" file &&
 	git tag b &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'Check "ours" is CRLF' '
diff --git a/t/t6414-merge-rename-nocruft.sh b/t/t6414-merge-rename-nocruft.sh
index a25e730..d7e3c1f 100755
--- a/t/t6414-merge-rename-nocruft.sh
+++ b/t/t6414-merge-rename-nocruft.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Merge-recursive merging renames'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -65,7 +68,7 @@
 	git update-index A &&
 	git commit -m "blue modify A" &&
 
-	git checkout master
+	git checkout main
 '
 
 # This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
diff --git a/t/t6415-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh
index 2eddcc7..2ce104a 100755
--- a/t/t6415-merge-dir-to-symlink.sh
+++ b/t/t6415-merge-dir-to-symlink.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='merging when a directory was replaced with a symlink'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'create a commit where dir a/b changed to symlink' '
@@ -19,7 +22,7 @@
 
 test_expect_success 'checkout does not clobber untracked symlink' '
 	git checkout HEAD^0 &&
-	git reset --hard master &&
+	git reset --hard main &&
 	git rm --cached a/b &&
 	git commit -m "untracked symlink remains" &&
 	test_must_fail git checkout start^0
@@ -27,7 +30,7 @@
 
 test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
 	git checkout HEAD^0 &&
-	git reset --hard master &&
+	git reset --hard main &&
 	git rm --cached a/b &&
 	git commit -m "untracked symlink remains" &&
 	git checkout -f start^0 &&
@@ -36,7 +39,7 @@
 
 test_expect_success 'checkout should not have deleted a/b-2/c/d' '
 	git checkout HEAD^0 &&
-	git reset --hard master &&
+	git reset --hard main &&
 	 git checkout start^0 &&
 	 test_path_is_file a/b-2/c/d
 '
@@ -53,7 +56,7 @@
 test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
-	git merge -s resolve master &&
+	git merge -s resolve main &&
 	test_path_is_file a/b-2/c/d
 '
 
@@ -64,7 +67,7 @@
 test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
-	git merge -s recursive master &&
+	git merge -s recursive main &&
 	test_path_is_file a/b-2/c/d
 '
 
@@ -74,7 +77,7 @@
 
 test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git merge -s resolve baseline^0 &&
 	test_path_is_file a/b-2/c/d
 '
@@ -85,7 +88,7 @@
 
 test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git merge -s recursive baseline^0 &&
 	test_path_is_file a/b-2/c/d
 '
@@ -98,7 +101,7 @@
 	git reset --hard &&
 	git checkout baseline^0 &&
 	>a/b/c/e &&
-	test_must_fail git merge -s resolve master &&
+	test_must_fail git merge -s resolve main &&
 	test_path_is_file a/b/c/e &&
 	test_path_is_file a/b-2/c/d
 '
@@ -107,7 +110,7 @@
 	git reset --hard &&
 	git checkout baseline^0 &&
 	>a/b/c/e &&
-	test_must_fail git merge -s recursive master &&
+	test_must_fail git merge -s recursive main &&
 	test_path_is_file a/b/c/e &&
 	test_path_is_file a/b-2/c/d
 '
@@ -116,14 +119,14 @@
 	git reset --hard &&
 	git checkout baseline^0 &&
 	echo more content >>a/b/c/d &&
-	test_must_fail git merge -s resolve master
+	test_must_fail git merge -s resolve main
 '
 
 test_expect_success 'do not lose modifications in merge (recursive)' '
 	git reset --hard &&
 	git checkout baseline^0 &&
 	echo more content >>a/b/c/d &&
-	test_must_fail git merge -s recursive master
+	test_must_fail git merge -s recursive main
 '
 
 test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index 887c219..84f5082 100755
--- a/t/t6416-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -2,6 +2,9 @@
 
 test_description='recursive merge corner cases involving criss-cross merges'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
@@ -385,7 +388,7 @@
 		test_line_count = 2 out &&
 
 		git rev-parse >expect       \
-			master:file  B:file &&
+			main:file    B:file &&
 		git rev-parse   >actual      \
 			:1:file      :2:file &&
 		test_cmp expect actual
@@ -407,7 +410,7 @@
 		test_line_count = 2 out &&
 
 		git rev-parse >expect       \
-			master:file  B:file &&
+			main:file    B:file &&
 		git rev-parse   >actual      \
 			:1:file      :3:file &&
 		test_cmp expect actual
@@ -1553,12 +1556,12 @@
 # Setup:
 #          L1---L2
 #         /  \ /  \
-#   master    X    ?
+#     main    X    ?
 #         \  / \  /
 #          R1---R2
 #
 # Where:
-#   master has two files, named 'b' and 'a'
+#   main has two files, named 'b' and 'a'
 #   branches L1 and R1 both modify each of the two files in conflicting ways
 #
 #   L2 is a merge of R1 into L1; more on it later.
@@ -1663,7 +1666,7 @@
 		cd nested_conflicts &&
 
 		git clean -f &&
-		MASTER=$(git rev-parse --short master) &&
+		MAIN=$(git rev-parse --short main) &&
 		git checkout L2^0 &&
 
 		# Merge must fail; there is a conflict
@@ -1679,24 +1682,24 @@
 		test_line_count = 1 out &&
 
 		# Create a and b from virtual merge base X
-		git cat-file -p master:a >base &&
+		git cat-file -p main:a >base &&
 		git cat-file -p L1:a >ours &&
 		git cat-file -p R1:a >theirs &&
 		test_must_fail git merge-file --diff3 \
 			-L "Temporary merge branch 1" \
-			-L "$MASTER"  \
+			-L "$MAIN"  \
 			-L "Temporary merge branch 2" \
 			ours  \
 			base  \
 			theirs &&
 		sed -e "s/^\([<|=>]\)/\1\1/" ours >vmb_a &&
 
-		git cat-file -p master:b >base &&
+		git cat-file -p main:b >base &&
 		git cat-file -p L1:b >ours &&
 		git cat-file -p R1:b >theirs &&
 		test_must_fail git merge-file --diff3 \
 			-L "Temporary merge branch 1" \
-			-L "$MASTER"  \
+			-L "$MAIN"  \
 			-L "Temporary merge branch 2" \
 			ours  \
 			base  \
@@ -1748,12 +1751,12 @@
 # Setup:
 #          L1---L2---L3
 #         /  \ /  \ /  \
-#   master    X1   X2   ?
+#     main    X1   X2   ?
 #         \  / \  / \  /
 #          R1---R2---R3
 #
 # Where:
-#   master has one file named 'content'
+#   main has one file named 'content'
 #   branches L1 and R1 both modify each of the two files in conflicting ways
 #
 #   L<n> (n>1) is a merge of R<n-1> into L<n-1>
@@ -1834,7 +1837,7 @@
 	(
 		cd virtual_merge_base_has_nested_conflicts &&
 
-		MASTER=$(git rev-parse --short master) &&
+		MAIN=$(git rev-parse --short main) &&
 		git checkout L3^0 &&
 
 		# Merge must fail; there is a conflict
@@ -1857,13 +1860,13 @@
 		# Imitate X1 merge base, except without long enough conflict
 		# markers because a subsequent sed will modify them.  Put
 		# result into vmb.
-		git cat-file -p master:content >base &&
+		git cat-file -p main:content >base &&
 		git cat-file -p L:content >left &&
 		git cat-file -p R:content >right &&
 		cp left merged-once &&
 		test_must_fail git merge-file --diff3 \
 			-L "Temporary merge branch 1" \
-			-L "$MASTER"  \
+			-L "$MAIN"  \
 			-L "Temporary merge branch 2" \
 			merged-once \
 			base        \
diff --git a/t/t6417-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh
index 0aebc6c..ac9aee9 100755
--- a/t/t6417-merge-ours-theirs.sh
+++ b/t/t6417-merge-ours-theirs.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Merge-recursive ours and theirs variants'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -20,11 +23,11 @@
 	sed -e "s/9/nueve/" >file <elif &&
 	git commit -a -m theirs &&
 
-	git checkout master^0
+	git checkout main^0
 '
 
 test_expect_success 'plain recursive - should conflict' '
-	git reset --hard master &&
+	git reset --hard main &&
 	test_must_fail git merge -s recursive side &&
 	grep nine file &&
 	grep nueve file &&
@@ -34,7 +37,7 @@
 '
 
 test_expect_success 'recursive favouring theirs' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git merge -s recursive -Xtheirs side &&
 	! grep nine file &&
 	grep nueve file &&
@@ -44,7 +47,7 @@
 '
 
 test_expect_success 'recursive favouring ours' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git merge -s recursive -X ours side &&
 	grep nine file &&
 	! grep nueve file &&
@@ -56,26 +59,26 @@
 test_expect_success 'binary file with -Xours/-Xtheirs' '
 	echo file binary >.gitattributes &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git merge -s recursive -X theirs side &&
 	git diff --exit-code side HEAD -- file &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git merge -s recursive -X ours side &&
-	git diff --exit-code master HEAD -- file
+	git diff --exit-code main HEAD -- file
 '
 
 test_expect_success 'pull passes -X to underlying merge' '
-	git reset --hard master && git pull -s recursive -Xours . side &&
-	git reset --hard master && git pull -s recursive -X ours . side &&
-	git reset --hard master && git pull -s recursive -Xtheirs . side &&
-	git reset --hard master && git pull -s recursive -X theirs . side &&
-	git reset --hard master && test_must_fail git pull -s recursive -X bork . side
+	git reset --hard main && git pull -s recursive -Xours . side &&
+	git reset --hard main && git pull -s recursive -X ours . side &&
+	git reset --hard main && git pull -s recursive -Xtheirs . side &&
+	git reset --hard main && git pull -s recursive -X theirs . side &&
+	git reset --hard main && test_must_fail git pull -s recursive -X bork . side
 '
 
 test_expect_success SYMLINKS 'symlink with -Xours/-Xtheirs' '
-	git reset --hard master &&
-	git checkout -b two master &&
+	git reset --hard main &&
+	git checkout -b two main &&
 	ln -s target-zero link &&
 	git add link &&
 	git commit -m "add link pointing to zero" &&
diff --git a/t/t6418-merge-text-auto.sh b/t/t6418-merge-text-auto.sh
index 30983d1..1e0296d 100755
--- a/t/t6418-merge-text-auto.sh
+++ b/t/t6418-merge-text-auto.sh
@@ -2,16 +2,19 @@
 
 test_description='CRLF merge conflict across text=auto change
 
-* [master] remove .gitattributes
+* [main] remove .gitattributes
  ! [side] add line from b
 --
  + [side] add line from b
-*  [master] remove .gitattributes
-*  [master^] add line from a
-*  [master~2] normalize file
+*  [main] remove .gitattributes
+*  [main^] add line from a
+*  [main~2] normalize file
 *+ [side^] Initial
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
@@ -64,7 +67,7 @@
 	git commit -m "add line from b" &&
 	git tag b &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'set up fuzz_conflict() helper' '
@@ -195,7 +198,7 @@
 	git reset --hard initial &&
 	git rm file &&
 	git commit -m "remove file" &&
-	git checkout master &&
+	git checkout main &&
 	git reset --hard a^ &&
 	git merge side &&
 	test_path_is_missing file
diff --git a/t/t6419-merge-ignorecase.sh b/t/t6419-merge-ignorecase.sh
index 531850d..b64b75a 100755
--- a/t/t6419-merge-ignorecase.sh
+++ b/t/t6419-merge-ignorecase.sh
@@ -2,6 +2,9 @@
 
 test_description='git-merge with case-changing rename on case-insensitive file system'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq CASE_INSENSITIVE_FS
@@ -20,18 +23,18 @@
 	>foo &&
 	git add foo &&
 	git commit -m "intervening commit" &&
-	git checkout master &&
+	git checkout main &&
 	git rm TestCase &&
 	>testcase &&
 	git add testcase &&
 	git commit -m "rename to testcase" &&
 	git checkout with-camel &&
-	git merge master -m "merge" &&
+	git merge main -m "merge" &&
 	test_path_is_file testcase
 '
 
 test_expect_success 'merge with case-changing rename on both sides' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard baseline &&
 	git branch -D with-camel &&
 	git checkout -b with-camel &&
@@ -40,13 +43,13 @@
 	>foo &&
 	git add foo &&
 	git commit -m "intervening commit" &&
-	git checkout master &&
+	git checkout main &&
 	git rm TestCase &&
 	>testcase &&
 	git add testcase &&
 	git commit -m "rename to testcase" &&
 	git checkout with-camel &&
-	git merge master -m "merge" &&
+	git merge main -m "merge" &&
 	test_path_is_file testcase
 '
 
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index 78bfaf1..bf4ce3c 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -3,6 +3,9 @@
 test_description="recursive merge corner cases w/ renames but not criss-crosses"
 # t6036 has corner cases that involve both criss-cross merges and renames
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
@@ -1130,7 +1133,7 @@
 	# Setup:
 	#          L
 	#         / \
-	#   master   ?
+	#     main   ?
 	#         \ /
 	#          R
 	#
@@ -1260,7 +1263,7 @@
 				:2:three           \
 				:3:three           &&
 			git rev-parse >expected        \
-				master:irrelevant_file \
+				main:irrelevant_file \
 				file_v2                \
 				file_v4                &&
 			test_cmp expected actual &&
@@ -1290,12 +1293,12 @@
 # Setup:
 #          L
 #         / \
-#   master   ?
+#     main   ?
 #         \ /
 #          R
 #
 # Where:
-#   master has two files, named 'one' and 'two'.
+#   main has two files, named 'one' and 'two'.
 #   branches L and R both modify 'one', in conflicting ways.
 #   branches L and R both modify 'two', in conflicting ways.
 #   branch L also renames 'one' to 'three'.
@@ -1376,7 +1379,7 @@
 		test_line_count = 1 out &&
 
 		# Compare :2:three to expected values
-		git cat-file -p master:one >base &&
+		git cat-file -p main:one >base &&
 		git cat-file -p L:three >ours &&
 		git cat-file -p R:one >theirs &&
 		test_must_fail git merge-file    \
@@ -1387,7 +1390,7 @@
 		test_cmp expect L-three &&
 
 		# Compare :2:three to expected values
-		git cat-file -p master:two >base &&
+		git cat-file -p main:two >base &&
 		git cat-file -p L:two >ours &&
 		git cat-file -p R:three >theirs &&
 		test_must_fail git merge-file    \
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 4ab133f..5d3b711 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -2905,7 +2905,7 @@
 	)
 }
 
-test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '
+test_expect_success '9e: N-to-1 whammo' '
 	test_setup_9e &&
 	(
 		cd 9e &&
diff --git a/t/t6425-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh
index f79d021..459b431 100755
--- a/t/t6425-merge-rename-delete.sh
+++ b/t/t6425-merge-rename-delete.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='Merge-recursive rename/delete conflict message'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'rename/delete' '
@@ -12,7 +15,7 @@
 	git mv A B &&
 	git commit -m "rename" &&
 
-	git checkout master &&
+	git checkout main &&
 	git rm A &&
 	git commit -m "delete" &&
 
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index d7eeee4..7b5f1c1 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -492,7 +492,9 @@
 		test_cmp expect actual &&
 
 		test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-		test_path_is_missing bq foo/bq foo/whatever
+		test_path_is_missing bq &&
+		test_path_is_missing foo/bq &&
+		test_path_is_missing foo/whatever
 	)
 '
 
@@ -522,7 +524,9 @@
 		test_cmp expect actual &&
 
 		test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-		test_path_is_missing bq foo/bq foo/whatever
+		test_path_is_missing bq &&
+		test_path_is_missing foo/bq &&
+		test_path_is_missing foo/whatever
 	)
 '
 
@@ -588,7 +592,9 @@
 		test_cmp expect actual &&
 
 		test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-		test_path_is_missing bq foo/bq foo/whatever
+		test_path_is_missing bq &&
+		test_path_is_missing foo/bq &&
+		test_path_is_missing foo/whatever
 	)
 '
 
@@ -618,7 +624,9 @@
 		test_cmp expect actual &&
 
 		test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-		test_path_is_missing bq foo/bq foo/whatever
+		test_path_is_missing bq &&
+		test_path_is_missing foo/bq &&
+		test_path_is_missing foo/whatever
 	)
 '
 
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index f4655bb..25c4b72 100755
--- a/t/t6427-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
@@ -2,6 +2,9 @@
 
 test_description='recursive merge diff3 style conflict markers'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Setup:
@@ -43,7 +46,7 @@
 # Setup:
 #          L1
 #         /  \
-#   master    ?
+#     main    ?
 #         \  /
 #          R1
 #
@@ -90,18 +93,18 @@
 		cd unique_merge_base &&
 
 		git checkout L^0 &&
-		MASTER=$(git rev-parse --short master) &&
+		MAIN=$(git rev-parse --short main) &&
 
 		test_must_fail git -c merge.conflictstyle=diff3 merge -s recursive R^0 &&
 
-		grep "|||||| $MASTER:content" renamed
+		grep "|||||| $MAIN:content" renamed
 	)
 '
 
 # Setup:
 #          L1---L2--L3
 #         /  \ /      \
-#   master    X1       ?
+#     main    X1       ?
 #         \  / \      /
 #          R1---R2--R3
 #
@@ -191,10 +194,10 @@
 	(
 		cd rebase &&
 		test_commit base file &&
-		test_commit master file &&
+		test_commit main file &&
 		git checkout -b side HEAD^ &&
 		test_commit side file &&
-		test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+		test_must_fail git -c merge.conflictstyle=diff3 rebase --merge main &&
 		grep "||||||| parent of" file
 	)
 '
@@ -203,7 +206,7 @@
 	(
 		cd rebase &&
 		git rebase --abort &&
-		test_must_fail git -c merge.conflictstyle=diff3 rebase --apply master &&
+		test_must_fail git -c merge.conflictstyle=diff3 rebase --apply main &&
 		grep "||||||| constructed merge base" file
 	)
 '
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index 9c08e63..ffcc01f 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -2,6 +2,9 @@
 
 test_description='merge-recursive backend test'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-merge.sh
 
@@ -35,7 +38,7 @@
 	git add a d/e &&
 
 	test_tick &&
-	git commit -m "master modifies a and d/e" &&
+	git commit -m "main modifies a and d/e" &&
 	c1=$(git rev-parse --verify HEAD) &&
 	( git ls-tree -r HEAD && git ls-files -s ) >actual &&
 	(
@@ -466,7 +469,7 @@
 		git checkout -b branch1 &&
 		git commit --allow-empty -m "empty commit" &&
 
-		git checkout master &&
+		git checkout main &&
 		git rm foo &&
 		mkdir foo &&
 		>foo/bar &&
@@ -475,7 +478,7 @@
 
 		git checkout branch1 &&
 
-		git cherry-pick master &&
+		git cherry-pick main &&
 		test_path_is_dir foo &&
 		test_path_is_file foo/bar
 	)
@@ -490,8 +493,8 @@
 
 test_expect_success 'reset and bind merge' '
 
-	git reset --hard master &&
-	git read-tree --prefix=M/ master &&
+	git reset --hard main &&
+	git read-tree --prefix=M/ main &&
 	git ls-files -s >actual &&
 	(
 		echo "100644 $o1 0	M/a" &&
@@ -505,7 +508,7 @@
 	) >expected &&
 	test_cmp expected actual &&
 
-	git read-tree --prefix=a1/ master &&
+	git read-tree --prefix=a1/ main &&
 	git ls-files -s >actual &&
 	(
 		echo "100644 $o1 0	M/a" &&
@@ -523,7 +526,7 @@
 	) >expected &&
 	test_cmp expected actual &&
 
-	git read-tree --prefix=z/ master &&
+	git read-tree --prefix=z/ main &&
 	git ls-files -s >actual &&
 	(
 		echo "100644 $o1 0	M/a" &&
@@ -599,11 +602,11 @@
 
 test_expect_success 'merge removes empty directories' '
 
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b rm &&
 	git rm d/e &&
 	git commit -mremoved-d/e &&
-	git checkout master &&
+	git checkout main &&
 	git merge -s recursive rm &&
 	test_path_is_missing d
 '
diff --git a/t/t6432-merge-recursive-space-options.sh b/t/t6432-merge-recursive-space-options.sh
index b56180e..db4b77e 100755
--- a/t/t6432-merge-recursive-space-options.sh
+++ b/t/t6432-merge-recursive-space-options.sh
@@ -2,15 +2,18 @@
 
 test_description='merge-recursive space options
 
-* [master] Clarify
+* [main] Clarify
  ! [remote] Remove cruft
 --
  + [remote] Remove cruft
-*  [master] Clarify
+*  [main] Clarify
 *+ [remote^] Initial revision
 *   ok 1: setup
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
@@ -87,7 +90,7 @@
 	mv text.txt+ text.txt &&
 	git commit -a -m "Remove cruft" &&
 
-	git checkout master &&
+	git checkout main &&
 	sed -e "
 			s/\(not in his right mind\),\(.*\)/\1;\2Q/
 			s/Quite correct\(.*\)/It is too correct\1Q/
diff --git a/t/t6433-merge-toplevel.sh b/t/t6433-merge-toplevel.sh
index e29c284..b160314 100755
--- a/t/t6433-merge-toplevel.sh
+++ b/t/t6433-merge-toplevel.sh
@@ -2,10 +2,13 @@
 
 test_description='"git merge" top-level frontend'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 t3033_reset () {
-	git checkout -B master two &&
+	git checkout -B main two &&
 	git branch -f left three &&
 	git branch -f right four
 }
@@ -21,7 +24,7 @@
 	test_commit four &&
 	git checkout --orphan newroot &&
 	test_commit five &&
-	git checkout master
+	git checkout main
 '
 
 # Local branches
@@ -61,7 +64,7 @@
 test_expect_success 'merge octopus, fast-forward (does not ff)' '
 	t3033_reset &&
 	git merge left right &&
-	# two (master) is not an ancestor of three (left) and four (right)
+	# two (main) is not an ancestor of three (left) and four (right)
 	test_must_fail git rev-parse --verify HEAD^4 &&
 	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
 	git rev-parse two three four | sort >expect &&
@@ -118,7 +121,7 @@
 	t3033_reset &&
 	git fetch . left right &&
 	git merge FETCH_HEAD &&
-	# two (master) is not an ancestor of three (left) and four (right)
+	# two (main) is not an ancestor of three (left) and four (right)
 	test_must_fail git rev-parse --verify HEAD^4 &&
 	git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
 	git rev-parse two three four | sort >expect &&
diff --git a/t/t6434-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh
index 3d9fae6..a117078 100755
--- a/t/t6434-merge-recursive-rename-options.sh
+++ b/t/t6434-merge-recursive-rename-options.sh
@@ -5,11 +5,11 @@
 Test rename detection by examining rename/delete conflicts.
 
 * (HEAD -> rename) rename
-| * (master) delete
+| * (main) delete
 |/
 * base
 
-git diff --name-status base master
+git diff --name-status base main
 D	0-old
 D	1-old
 D	2-old
@@ -26,6 +26,9 @@
 mentions this in a different context).
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 get_expected_stages () {
@@ -118,7 +121,7 @@
 	get_expected_stages 2 &&
 	get_expected_stages 3 &&
 	check_50="false" &&
-	tail="HEAD^ -- HEAD master"
+	tail="HEAD^ -- HEAD main"
 '
 
 test_expect_success 'setup thresholds' '
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index dd93768..84b4aac 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -4,6 +4,9 @@
 
 Do not overwrite changes.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -136,7 +139,7 @@
 	cp important sub &&
 	cp important sub2 &&
 	test_must_fail git merge sub 2>out &&
-	test_i18ncmp out expect &&
+	test_cmp out expect &&
 	test_path_is_missing .git/MERGE_HEAD &&
 	test_cmp important sub &&
 	test_cmp important sub2 &&
@@ -171,7 +174,7 @@
 	git checkout --orphan new &&
 	cp important c0.c &&
 	test_must_fail git merge c0 2>out &&
-	test_i18ncmp out expect
+	test_cmp out expect
 '
 
 test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' '
@@ -193,7 +196,7 @@
 '
 
 test_expect_success 'will not clobber WT/index when merging into unborn' '
-	git merge master &&
+	git merge main &&
 	grep foo tracked-file &&
 	git show :tracked-file >expect &&
 	grep foo expect &&
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 3ead2b7..0f92bcf 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -2,6 +2,9 @@
 
 test_description='merging with submodules'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 #
@@ -27,7 +30,7 @@
 	test_tick &&
 	git commit -m root &&
 
-	git checkout -b a master &&
+	git checkout -b a main &&
 	(cd sub &&
 	 echo A > file &&
 	 git add file &&
@@ -37,7 +40,7 @@
 	test_tick &&
 	git commit -m a &&
 
-	git checkout -b b master &&
+	git checkout -b b main &&
 	(cd sub &&
 	 echo B > file &&
 	 git add file &&
@@ -254,22 +257,22 @@
 	 (cd sub &&
 	  git init &&
 	  test_commit a &&
-	  git checkout -b sub-b master &&
+	  git checkout -b sub-b main &&
 	  test_commit b &&
-	  git checkout -b sub-c master &&
+	  git checkout -b sub-c main &&
 	  test_commit c &&
 	  git checkout -b sub-bc sub-b &&
 	  git merge sub-c &&
 	  git checkout -b sub-cb sub-c &&
 	  git merge sub-b &&
-	  git checkout master) &&
+	  git checkout main) &&
 	 git add sub &&
 	 git commit -m a &&
-	 git checkout -b top-b master &&
+	 git checkout -b top-b main &&
 	 (cd sub && git checkout sub-b) &&
 	 git add sub &&
 	 git commit -m b &&
-	 git checkout -b top-c master &&
+	 git checkout -b top-c main &&
 	 (cd sub && git checkout sub-c) &&
 	 git add sub &&
 	 git commit -m c &&
diff --git a/t/t6439-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh
index 5c8894d..5bfb027 100755
--- a/t/t6439-merge-co-error-msgs.sh
+++ b/t/t6439-merge-co-error-msgs.sh
@@ -2,6 +2,9 @@
 
 test_description='unpack-trees error messages'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
@@ -18,7 +21,7 @@
 	git add two three four five &&
 	git commit -m Second &&
 
-	git checkout master &&
+	git checkout main &&
 	echo other >two &&
 	echo other >three &&
 	echo other >four &&
@@ -37,14 +40,14 @@
 
 test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
 	test_must_fail git merge branch 2>out &&
-	test_i18ncmp out expect &&
+	test_cmp out expect &&
 	git commit --allow-empty -m empty &&
 	(
 		GIT_MERGE_VERBOSITY=0 &&
 		export GIT_MERGE_VERBOSITY &&
 		test_must_fail git merge branch 2>out2
 	) &&
-	test_i18ncmp out2 expect &&
+	test_cmp out2 expect &&
 	git reset --hard HEAD^
 '
 
@@ -65,7 +68,7 @@
 	git add three &&
 	git add four &&
 	test_must_fail git merge branch 2>out &&
-	test_i18ncmp out expect
+	test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -83,11 +86,11 @@
 	echo two >rep/two &&
 	git add rep/one rep/two &&
 	git commit -m Fourth &&
-	git checkout master &&
+	git checkout main &&
 	echo uno >rep/one &&
 	echo dos >rep/two &&
 	test_must_fail git checkout branch 2>out &&
-	test_i18ncmp out expect
+	test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -101,7 +104,7 @@
 test_expect_success 'not uptodate file porcelain checkout error' '
 	git add rep/one rep/two &&
 	test_must_fail git checkout branch 2>out &&
-	test_i18ncmp out expect
+	test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -128,11 +131,11 @@
 	>rep2 &&
 	git add rep rep2 &&
 	git commit -m "added test as a file" &&
-	git checkout master &&
+	git checkout main &&
 	>rep/untracked-file &&
 	>rep2/untracked-file &&
 	test_must_fail git checkout branch 2>out &&
-	test_i18ncmp out ../expect
+	test_cmp out ../expect
 '
 
 test_done
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 4a3b8f4..60d961b 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -78,7 +78,7 @@
 		git gc &&
 		( cd .git/objects/pack && ls *.pack ) >pack-list &&
 		test_line_count = 1 pack-list &&
-		BASE_PACK=.git/objects/pack/pack-*.pack &&
+		cp pack-list base-pack-list &&
 		test_commit four &&
 		git repack -d &&
 		test_commit five &&
@@ -90,7 +90,7 @@
 		test_line_count = 2 pack-list &&
 		awk "/^P /{print \$2}" <.git/objects/info/packs >pack-info &&
 		test_line_count = 2 pack-info &&
-		test_path_is_file $BASE_PACK &&
+		test_path_is_file .git/objects/pack/$(cat base-pack-list) &&
 		git fsck
 	)
 '
@@ -106,17 +106,17 @@
 	test_commit "$(test_oid obj2)" &&
 	# Our first gc will create a pack; our second will create a second pack
 	git gc --auto &&
-	ls .git/objects/pack | sort >existing_packs &&
+	ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
 	test_commit "$(test_oid obj3)" &&
 	test_commit "$(test_oid obj4)" &&
 
 	git gc --auto 2>err &&
 	test_i18ngrep ! "^warning:" err &&
-	ls .git/objects/pack/ | sort >post_packs &&
+	ls .git/objects/pack/pack-*.pack | sort >post_packs &&
 	comm -1 -3 existing_packs post_packs >new &&
 	comm -2 -3 existing_packs post_packs >del &&
 	test_line_count = 0 del && # No packs are deleted
-	test_line_count = 2 new # There is one new pack and its .idx
+	test_line_count = 1 new # There is one new pack
 '
 
 test_expect_success 'gc --no-quiet' '
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index 8a3bb41..75210f0 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -25,6 +25,9 @@
 #      to refer to an existing tree).
 
 test_description='check pruning of dependent objects'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # We care about reachability, so we do not want to use
@@ -67,7 +70,7 @@
 		git checkout -b experiment &&
 		commit abandon &&
 		maybe_repack &&
-		git checkout master &&
+		git checkout main &&
 		git branch -D experiment
 	'
 
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index f807276..e2d33a8 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -55,10 +55,13 @@
 	git show-ref -s commit-5-5 | git commit-graph write --stdin-commits &&
 	mv .git/objects/info/commit-graph commit-graph-half &&
 	chmod u+w commit-graph-half &&
+	GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable &&
+	mv .git/objects/info/commit-graph commit-graph-no-gdat &&
+	chmod u+w commit-graph-no-gdat &&
 	git config core.commitGraph true
 '
 
-run_three_modes () {
+run_all_modes () {
 	test_when_finished rm -rf .git/objects/info/commit-graph &&
 	"$@" <input >actual &&
 	test_cmp expect actual &&
@@ -67,11 +70,14 @@
 	test_cmp expect actual &&
 	cp commit-graph-half .git/objects/info/commit-graph &&
 	"$@" <input >actual &&
+	test_cmp expect actual &&
+	cp commit-graph-no-gdat .git/objects/info/commit-graph &&
+	"$@" <input >actual &&
 	test_cmp expect actual
 }
 
-test_three_modes () {
-	run_three_modes test-tool reach "$@"
+test_all_modes () {
+	run_all_modes test-tool reach "$@"
 }
 
 test_expect_success 'ref_newer:miss' '
@@ -80,7 +86,7 @@
 	B:commit-4-9
 	EOF
 	echo "ref_newer(A,B):0" >expect &&
-	test_three_modes ref_newer
+	test_all_modes ref_newer
 '
 
 test_expect_success 'ref_newer:hit' '
@@ -89,7 +95,7 @@
 	B:commit-2-3
 	EOF
 	echo "ref_newer(A,B):1" >expect &&
-	test_three_modes ref_newer
+	test_all_modes ref_newer
 '
 
 test_expect_success 'in_merge_bases:hit' '
@@ -98,7 +104,7 @@
 	B:commit-8-8
 	EOF
 	echo "in_merge_bases(A,B):1" >expect &&
-	test_three_modes in_merge_bases
+	test_all_modes in_merge_bases
 '
 
 test_expect_success 'in_merge_bases:miss' '
@@ -107,7 +113,7 @@
 	B:commit-5-9
 	EOF
 	echo "in_merge_bases(A,B):0" >expect &&
-	test_three_modes in_merge_bases
+	test_all_modes in_merge_bases
 '
 
 test_expect_success 'in_merge_bases_many:hit' '
@@ -117,7 +123,7 @@
 	X:commit-5-7
 	EOF
 	echo "in_merge_bases_many(A,X):1" >expect &&
-	test_three_modes in_merge_bases_many
+	test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'in_merge_bases_many:miss' '
@@ -127,7 +133,7 @@
 	X:commit-8-6
 	EOF
 	echo "in_merge_bases_many(A,X):0" >expect &&
-	test_three_modes in_merge_bases_many
+	test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'in_merge_bases_many:miss-heuristic' '
@@ -137,7 +143,7 @@
 	X:commit-6-6
 	EOF
 	echo "in_merge_bases_many(A,X):0" >expect &&
-	test_three_modes in_merge_bases_many
+	test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'is_descendant_of:hit' '
@@ -148,7 +154,7 @@
 	X:commit-1-1
 	EOF
 	echo "is_descendant_of(A,X):1" >expect &&
-	test_three_modes is_descendant_of
+	test_all_modes is_descendant_of
 '
 
 test_expect_success 'is_descendant_of:miss' '
@@ -159,7 +165,7 @@
 	X:commit-7-6
 	EOF
 	echo "is_descendant_of(A,X):0" >expect &&
-	test_three_modes is_descendant_of
+	test_all_modes is_descendant_of
 '
 
 test_expect_success 'get_merge_bases_many' '
@@ -174,7 +180,7 @@
 		git rev-parse commit-5-6 \
 			      commit-4-7 | sort
 	} >expect &&
-	test_three_modes get_merge_bases_many
+	test_all_modes get_merge_bases_many
 '
 
 test_expect_success 'reduce_heads' '
@@ -196,7 +202,7 @@
 			      commit-2-8 \
 			      commit-1-10 | sort
 	} >expect &&
-	test_three_modes reduce_heads
+	test_all_modes reduce_heads
 '
 
 test_expect_success 'can_all_from_reach:hit' '
@@ -219,7 +225,7 @@
 	Y:commit-8-1
 	EOF
 	echo "can_all_from_reach(X,Y):1" >expect &&
-	test_three_modes can_all_from_reach
+	test_all_modes can_all_from_reach
 '
 
 test_expect_success 'can_all_from_reach:miss' '
@@ -241,7 +247,7 @@
 	Y:commit-8-5
 	EOF
 	echo "can_all_from_reach(X,Y):0" >expect &&
-	test_three_modes can_all_from_reach
+	test_all_modes can_all_from_reach
 '
 
 test_expect_success 'can_all_from_reach_with_flag: tags case' '
@@ -264,7 +270,7 @@
 	Y:commit-8-1
 	EOF
 	echo "can_all_from_reach_with_flag(X,_,_,0,0):1" >expect &&
-	test_three_modes can_all_from_reach_with_flag
+	test_all_modes can_all_from_reach_with_flag
 '
 
 test_expect_success 'commit_contains:hit' '
@@ -280,8 +286,8 @@
 	X:commit-9-3
 	EOF
 	echo "commit_contains(_,A,X,_):1" >expect &&
-	test_three_modes commit_contains &&
-	test_three_modes commit_contains --tag
+	test_all_modes commit_contains &&
+	test_all_modes commit_contains --tag
 '
 
 test_expect_success 'commit_contains:miss' '
@@ -297,8 +303,8 @@
 	X:commit-9-3
 	EOF
 	echo "commit_contains(_,A,X,_):0" >expect &&
-	test_three_modes commit_contains &&
-	test_three_modes commit_contains --tag
+	test_all_modes commit_contains &&
+	test_all_modes commit_contains --tag
 '
 
 test_expect_success 'rev-list: basic topo-order' '
@@ -310,7 +316,7 @@
 		commit-6-2 commit-5-2 commit-4-2 commit-3-2 commit-2-2 commit-1-2 \
 		commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
 	>expect &&
-	run_three_modes git rev-list --topo-order commit-6-6
+	run_all_modes git rev-list --topo-order commit-6-6
 '
 
 test_expect_success 'rev-list: first-parent topo-order' '
@@ -322,7 +328,7 @@
 		commit-6-2 \
 		commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
 	>expect &&
-	run_three_modes git rev-list --first-parent --topo-order commit-6-6
+	run_all_modes git rev-list --first-parent --topo-order commit-6-6
 '
 
 test_expect_success 'rev-list: range topo-order' '
@@ -334,7 +340,7 @@
 		commit-6-2 commit-5-2 commit-4-2 \
 		commit-6-1 commit-5-1 commit-4-1 \
 	>expect &&
-	run_three_modes git rev-list --topo-order commit-3-3..commit-6-6
+	run_all_modes git rev-list --topo-order commit-3-3..commit-6-6
 '
 
 test_expect_success 'rev-list: range topo-order' '
@@ -346,7 +352,7 @@
 		commit-6-2 commit-5-2 commit-4-2 \
 		commit-6-1 commit-5-1 commit-4-1 \
 	>expect &&
-	run_three_modes git rev-list --topo-order commit-3-8..commit-6-6
+	run_all_modes git rev-list --topo-order commit-3-8..commit-6-6
 '
 
 test_expect_success 'rev-list: first-parent range topo-order' '
@@ -358,7 +364,7 @@
 		commit-6-2 \
 		commit-6-1 commit-5-1 commit-4-1 \
 	>expect &&
-	run_three_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
+	run_all_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
 '
 
 test_expect_success 'rev-list: ancestry-path topo-order' '
@@ -368,7 +374,7 @@
 		commit-6-4 commit-5-4 commit-4-4 commit-3-4 \
 		commit-6-3 commit-5-3 commit-4-3 \
 	>expect &&
-	run_three_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
+	run_all_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
 '
 
 test_expect_success 'rev-list: symmetric difference topo-order' '
@@ -382,7 +388,7 @@
 		commit-3-8 commit-2-8 commit-1-8 \
 		commit-3-7 commit-2-7 commit-1-7 \
 	>expect &&
-	run_three_modes git rev-list --topo-order commit-3-8...commit-6-6
+	run_all_modes git rev-list --topo-order commit-3-8...commit-6-6
 '
 
 test_expect_success 'get_reachable_subset:all' '
@@ -402,7 +408,7 @@
 			      commit-1-7 \
 			      commit-5-6 | sort
 	) >expect &&
-	test_three_modes get_reachable_subset
+	test_all_modes get_reachable_subset
 '
 
 test_expect_success 'get_reachable_subset:some' '
@@ -420,7 +426,7 @@
 		git rev-parse commit-3-3 \
 			      commit-1-7 | sort
 	) >expect &&
-	test_three_modes get_reachable_subset
+	test_all_modes get_reachable_subset
 '
 
 test_expect_success 'get_reachable_subset:none' '
@@ -434,7 +440,7 @@
 	Y:commit-2-8
 	EOF
 	echo "get_reachable_subset(X,Y)" >expect &&
-	test_three_modes get_reachable_subset
+	test_all_modes get_reachable_subset
 '
 
 test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 63d5f41..25bb9bb 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -3,74 +3,74 @@
 test_description='git mv in subdirs'
 . ./test-lib.sh
 
-test_expect_success \
-    'prepare reference tree' \
-    'mkdir path0 path1 &&
-     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
-     git add path0/COPYING &&
-     git commit -m add -a'
+test_expect_success 'prepare reference tree' '
+	mkdir path0 path1 &&
+	cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+	git add path0/COPYING &&
+	git commit -m add -a
+'
 
-test_expect_success \
-    'moving the file out of subdirectory' \
-    'cd path0 && git mv COPYING ../path1/COPYING'
+test_expect_success 'moving the file out of subdirectory' '
+	git -C path0 mv COPYING ../path1/COPYING
+'
 
 # in path0 currently
-test_expect_success \
-    'commiting the change' \
-    'cd .. && git commit -m move-out -a'
+test_expect_success 'commiting the change' '
+	git commit -m move-out -a
+'
 
-test_expect_success \
-    'checking the commit' \
-    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
-    grep "^R100..*path0/COPYING..*path1/COPYING" actual'
+test_expect_success 'checking the commit' '
+	git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
+	grep "^R100..*path0/COPYING..*path1/COPYING" actual
+'
 
-test_expect_success \
-    'moving the file back into subdirectory' \
-    'cd path0 && git mv ../path1/COPYING COPYING'
+test_expect_success 'moving the file back into subdirectory' '
+	git -C path0 mv ../path1/COPYING COPYING
+'
 
 # in path0 currently
-test_expect_success \
-    'commiting the change' \
-    'cd .. && git commit -m move-in -a'
+test_expect_success 'commiting the change' '
+	git commit -m move-in -a
+'
 
-test_expect_success \
-    'checking the commit' \
-    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
-    grep "^R100..*path1/COPYING..*path0/COPYING" actual'
+test_expect_success 'checking the commit' '
+	git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
+	grep "^R100..*path1/COPYING..*path0/COPYING" actual
+'
 
-test_expect_success \
-    'mv --dry-run does not move file' \
-    'git mv -n path0/COPYING MOVED &&
-     test -f path0/COPYING &&
-     test ! -f MOVED'
+test_expect_success 'mv --dry-run does not move file' '
+	git mv -n path0/COPYING MOVED &&
+	test -f path0/COPYING &&
+	test ! -f MOVED
+'
 
-test_expect_success \
-    'checking -k on non-existing file' \
-    'git mv -k idontexist path0'
+test_expect_success 'checking -k on non-existing file' '
+	git mv -k idontexist path0
+'
 
-test_expect_success \
-    'checking -k on untracked file' \
-    'touch untracked1 &&
-     git mv -k untracked1 path0 &&
-     test -f untracked1 &&
-     test ! -f path0/untracked1'
+test_expect_success 'checking -k on untracked file' '
+	>untracked1 &&
+	git mv -k untracked1 path0 &&
+	test -f untracked1 &&
+	test ! -f path0/untracked1
+'
 
-test_expect_success \
-    'checking -k on multiple untracked files' \
-    'touch untracked2 &&
-     git mv -k untracked1 untracked2 path0 &&
-     test -f untracked1 &&
-     test -f untracked2 &&
-     test ! -f path0/untracked1 &&
-     test ! -f path0/untracked2'
+test_expect_success 'checking -k on multiple untracked files' '
+	>untracked2 &&
+	git mv -k untracked1 untracked2 path0 &&
+	test -f untracked1 &&
+	test -f untracked2 &&
+	test ! -f path0/untracked1 &&
+	test ! -f path0/untracked2
+'
 
-test_expect_success \
-    'checking -f on untracked file with existing target' \
-    'touch path0/untracked1 &&
-     test_must_fail git mv -f untracked1 path0 &&
-     test ! -f .git/index.lock &&
-     test -f untracked1 &&
-     test -f path0/untracked1'
+test_expect_success 'checking -f on untracked file with existing target' '
+	>path0/untracked1 &&
+	test_must_fail git mv -f untracked1 path0 &&
+	test ! -f .git/index.lock &&
+	test -f untracked1 &&
+	test -f path0/untracked1
+'
 
 # clean up the mess in case bad things happen
 rm -f idontexist untracked1 untracked2 \
@@ -78,87 +78,89 @@
      .git/index.lock
 rmdir path1
 
-test_expect_success \
-    'moving to absent target with trailing slash' \
-    'test_must_fail git mv path0/COPYING no-such-dir/ &&
-     test_must_fail git mv path0/COPYING no-such-dir// &&
-     git mv path0/ no-such-dir/ &&
-     test_path_is_dir no-such-dir'
+test_expect_success 'moving to absent target with trailing slash' '
+	test_must_fail git mv path0/COPYING no-such-dir/ &&
+	test_must_fail git mv path0/COPYING no-such-dir// &&
+	git mv path0/ no-such-dir/ &&
+	test_path_is_dir no-such-dir
+'
 
-test_expect_success \
-    'clean up' \
-    'git reset --hard'
+test_expect_success 'clean up' '
+	git reset --hard
+'
 
-test_expect_success \
-    'moving to existing untracked target with trailing slash' \
-    'mkdir path1 &&
-     git mv path0/ path1/ &&
-     test_path_is_dir path1/path0/'
+test_expect_success 'moving to existing untracked target with trailing slash' '
+	mkdir path1 &&
+	git mv path0/ path1/ &&
+	test_path_is_dir path1/path0/
+'
 
-test_expect_success \
-    'moving to existing tracked target with trailing slash' \
-    'mkdir path2 &&
-     >path2/file && git add path2/file &&
-     git mv path1/path0/ path2/ &&
-     test_path_is_dir path2/path0/'
+test_expect_success 'moving to existing tracked target with trailing slash' '
+	mkdir path2 &&
+	>path2/file && git add path2/file &&
+	git mv path1/path0/ path2/ &&
+	test_path_is_dir path2/path0/
+'
 
-test_expect_success \
-    'clean up' \
-    'git reset --hard'
+test_expect_success 'clean up' '
+	git reset --hard
+'
 
-test_expect_success \
-    'adding another file' \
-    'cp "$TEST_DIRECTORY"/../README.md path0/README &&
-     git add path0/README &&
-     git commit -m add2 -a'
+test_expect_success 'adding another file' '
+	cp "$TEST_DIRECTORY"/../README.md path0/README &&
+	git add path0/README &&
+	git commit -m add2 -a
+'
 
-test_expect_success \
-    'moving whole subdirectory' \
-    'git mv path0 path2'
+test_expect_success 'moving whole subdirectory' '
+	git mv path0 path2
+'
 
-test_expect_success \
-    'commiting the change' \
-    'git commit -m dir-move -a'
+test_expect_success 'commiting the change' '
+	git commit -m dir-move -a
+'
 
-test_expect_success \
-    'checking the commit' \
-    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
-     grep "^R100..*path0/COPYING..*path2/COPYING" actual &&
-     grep "^R100..*path0/README..*path2/README" actual'
+test_expect_success 'checking the commit' '
+	git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
+	grep "^R100..*path0/COPYING..*path2/COPYING" actual &&
+	grep "^R100..*path0/README..*path2/README" actual
+'
 
-test_expect_success \
-    'succeed when source is a prefix of destination' \
-    'git mv path2/COPYING path2/COPYING-renamed'
+test_expect_success 'succeed when source is a prefix of destination' '
+	git mv path2/COPYING path2/COPYING-renamed
+'
 
-test_expect_success \
-    'moving whole subdirectory into subdirectory' \
-    'git mv path2 path1'
+test_expect_success 'moving whole subdirectory into subdirectory' '
+	git mv path2 path1
+'
 
-test_expect_success \
-    'commiting the change' \
-    'git commit -m dir-move -a'
+test_expect_success 'commiting the change' '
+	git commit -m dir-move -a
+'
 
-test_expect_success \
-    'checking the commit' \
-    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
-     grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual &&
-     grep "^R100..*path2/README..*path1/path2/README" actual'
+test_expect_success 'checking the commit' '
+	git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
+	grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual &&
+	grep "^R100..*path2/README..*path1/path2/README" actual
+'
 
-test_expect_success \
-    'do not move directory over existing directory' \
-    'mkdir path0 && mkdir path0/path2 && test_must_fail git mv path2 path0'
+test_expect_success 'do not move directory over existing directory' '
+	mkdir path0 &&
+	mkdir path0/path2 &&
+	test_must_fail git mv path2 path0
+'
 
-test_expect_success \
-    'move into "."' \
-    'git mv path1/path2/ .'
+test_expect_success 'move into "."' '
+	git mv path1/path2/ .
+'
 
 test_expect_success "Michael Cassar's test case" '
 	rm -fr .git papers partA &&
 	git init &&
 	mkdir -p papers/unsorted papers/all-papers partA &&
-	echo a > papers/unsorted/Thesis.pdf &&
-	echo b > partA/outline.txt &&
-	echo c > papers/unsorted/_another &&
+	echo a >papers/unsorted/Thesis.pdf &&
+	echo b >partA/outline.txt &&
+	echo c >papers/unsorted/_another &&
 	git add papers partA &&
 	T1=$(git write-tree) &&
 
@@ -181,43 +183,42 @@
 	git mv ab a
 '
 
-test_expect_success 'absolute pathname' '(
+test_expect_success 'absolute pathname' '
+	(
+		rm -fr mine &&
+		mkdir mine &&
+		cd mine &&
+		test_create_repo one &&
+		cd one &&
+		mkdir sub &&
+		>sub/file &&
+		git add sub/file &&
 
-	rm -fr mine &&
-	mkdir mine &&
-	cd mine &&
-	test_create_repo one &&
-	cd one &&
-	mkdir sub &&
-	>sub/file &&
-	git add sub/file &&
+		git mv sub "$(pwd)/in" &&
+		! test -d sub &&
+		test -d in &&
+		git ls-files --error-unmatch in/file
+	)
+'
 
-	git mv sub "$(pwd)/in" &&
-	! test -d sub &&
-	test -d in &&
-	git ls-files --error-unmatch in/file
+test_expect_success 'absolute pathname outside should fail' '
+	(
+		rm -fr mine &&
+		mkdir mine &&
+		cd mine &&
+		out=$(pwd) &&
+		test_create_repo one &&
+		cd one &&
+		mkdir sub &&
+		>sub/file &&
+		git add sub/file &&
 
-
-)'
-
-test_expect_success 'absolute pathname outside should fail' '(
-
-	rm -fr mine &&
-	mkdir mine &&
-	cd mine &&
-	out=$(pwd) &&
-	test_create_repo one &&
-	cd one &&
-	mkdir sub &&
-	>sub/file &&
-	git add sub/file &&
-
-	test_must_fail git mv sub "$out/out" &&
-	test -d sub &&
-	! test -d ../in &&
-	git ls-files --error-unmatch sub/file
-
-)'
+		test_must_fail git mv sub "$out/out" &&
+		test -d sub &&
+		! test -d ../in &&
+		git ls-files --error-unmatch sub/file
+	)
+'
 
 test_expect_success 'git mv to move multiple sources into a directory' '
 	rm -fr .git && git init &&
@@ -227,23 +228,24 @@
 	git add dir/?.txt &&
 	git mv dir/a.txt dir/b.txt other &&
 	git ls-files >actual &&
-	{ echo other/a.txt; echo other/b.txt; } >expect &&
+	cat >expect <<-\EOF &&
+	other/a.txt
+	other/b.txt
+	EOF
 	test_cmp expect actual
 '
 
 test_expect_success 'git mv should not change sha1 of moved cache entry' '
-
 	rm -fr .git &&
 	git init &&
 	echo 1 >dirty &&
 	git add dirty &&
 	entry="$(git ls-files --stage dirty | cut -f 1)" &&
 	git mv dirty dirty2 &&
-	[ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
+	test "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" &&
 	echo 2 >dirty2 &&
 	git mv dirty2 dirty &&
-	[ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ]
-
+	test "$entry" = "$(git ls-files --stage dirty | cut -f 1)"
 '
 
 rm -f dirty dirty2
@@ -266,7 +268,6 @@
 '
 
 test_expect_success 'git mv should overwrite symlink to a file' '
-
 	rm -fr .git &&
 	git init &&
 	echo 1 >moved &&
@@ -279,13 +280,11 @@
 	test "$(cat symlink)" = 1 &&
 	git update-index --refresh &&
 	git diff-files --quiet
-
 '
 
 rm -f moved symlink
 
 test_expect_success 'git mv should overwrite file with a symlink' '
-
 	rm -fr .git &&
 	git init &&
 	echo 1 >moved &&
@@ -296,11 +295,9 @@
 	! test -e symlink &&
 	git update-index --refresh &&
 	git diff-files --quiet
-
 '
 
 test_expect_success SYMLINKS 'check moved symlink' '
-
 	test -h moved
 '
 
@@ -335,11 +332,8 @@
 	mkdir mod &&
 	git mv sub mod/sub &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
 '
@@ -358,11 +352,8 @@
 	mkdir mod &&
 	git mv sub mod/sub &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	echo mod/sub >expected &&
 	git config -f .gitmodules submodule.sub.path >actual &&
 	test_cmp expected actual &&
@@ -376,16 +367,10 @@
 	git submodule update &&
 	entry="$(git ls-files --stage sub | cut -f 1)" &&
 	mkdir mod &&
-	(
-		cd mod &&
-		git mv ../sub/ .
-	) &&
+	git -C mod mv ../sub/ . &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	echo mod/sub >expected &&
 	git config -f .gitmodules submodule.sub.path >actual &&
 	test_cmp expected actual &&
@@ -403,11 +388,8 @@
 	git mv sub mod/sub 2>actual.err &&
 	test_must_be_empty actual.err &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
 '
@@ -427,11 +409,8 @@
 	git mv sub mod/sub 2>actual.err &&
 	test_must_be_empty actual.err &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
 '
@@ -446,13 +425,10 @@
 	echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
 	mkdir mod &&
 	git mv sub mod/sub 2>actual.err &&
-	test_i18ncmp expect.err actual.err &&
+	test_cmp expect.err actual.err &&
 	! test -e sub &&
-	[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
-	(
-		cd mod/sub &&
-		git status
-	) &&
+	test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+	git -C mod/sub status &&
 	git update-index --refresh &&
 	git diff-files --quiet
 '
@@ -515,15 +491,17 @@
 test_expect_success 'moving nested submodules' '
 	git commit -am "cleanup commit" &&
 	mkdir sub_nested_nested &&
-	(cd sub_nested_nested &&
-		touch nested_level2 &&
+	(
+		cd sub_nested_nested &&
+		>nested_level2 &&
 		git init &&
 		git add . &&
 		git commit -m "nested level 2"
 	) &&
 	mkdir sub_nested &&
-	(cd sub_nested &&
-		touch nested_level1 &&
+	(
+		cd sub_nested &&
+		>nested_level1 &&
 		git init &&
 		git add . &&
 		git commit -m "nested level 1" &&
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 36477cb..1c55695 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git filter-branch'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -13,7 +16,7 @@
 	mkdir dir &&
 	test_commit dir/D &&
 	test_commit E &&
-	git checkout master &&
+	git checkout main &&
 	test_commit C &&
 	git checkout branch &&
 	git merge C &&
@@ -25,7 +28,7 @@
 # * G
 # *   Merge commit 'C' into branch
 # |\
-# | * (master) C
+# | * (main) C
 # * | E
 # * | dir/D
 # * | D
@@ -162,15 +165,15 @@
 '
 
 test_expect_success 'more setup' '
-	git checkout master &&
+	git checkout main &&
 	mkdir subdir &&
 	echo A > subdir/new &&
 	git add subdir/new &&
 	test_tick &&
-	git commit -m "subdir on master" subdir/new &&
+	git commit -m "subdir on main" subdir/new &&
 	git rm A.t &&
 	test_tick &&
-	git commit -m "again subdir on master" &&
+	git commit -m "again subdir on main" &&
 	git merge branch
 '
 
@@ -199,7 +202,7 @@
 	git branch preserved-author &&
 	(sane_unset GIT_AUTHOR_NAME &&
 	 git filter-branch -f --msg-filter "cat; \
-			test \$GIT_COMMIT != $(git rev-parse master) || \
+			test \$GIT_COMMIT != $(git rev-parse main) || \
 			echo Hallo" \
 		preserved-author) &&
 	git rev-list --author="B V Uips" preserved-author >actual &&
@@ -218,7 +221,7 @@
 		else\
 			git commit-tree \"\$@\";\
 		fi" removed-author &&
-	cnt1=$(git rev-list master | wc -l) &&
+	cnt1=$(git rev-list main | wc -l) &&
 	cnt2=$(git rev-list removed-author | wc -l) &&
 	test $cnt1 -eq $(($cnt2 + 1)) &&
 	git rev-list --author="B V Uips" removed-author >actual &&
@@ -226,7 +229,7 @@
 '
 
 test_expect_success 'barf on invalid name' '
-	test_must_fail git filter-branch -f master xy-problem &&
+	test_must_fail git filter-branch -f main xy-problem &&
 	test_must_fail git filter-branch -f HEAD^
 '
 
@@ -236,8 +239,8 @@
 		mapped=\$(map \$parent) &&
 		actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") &&
 		test \$mapped = \$actual &&
-		git commit-tree \"\$@\";" master~2..master &&
-	git rev-parse --verify master
+		git commit-tree \"\$@\";" main~2..main &&
+	git rev-parse --verify main
 '
 
 test_expect_success 'Name needing quotes' '
@@ -256,7 +259,7 @@
 
 test_expect_success 'Subdirectory filter with disappearing trees' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	mkdir foo &&
 	touch foo/bar &&
@@ -275,7 +278,7 @@
 	git commit -m "Re-adding foo" &&
 
 	git filter-branch -f --subdirectory-filter foo &&
-	git rev-list master >actual &&
+	git rev-list main >actual &&
 	test_line_count = 3 actual
 '
 
@@ -332,7 +335,7 @@
 	test_cmp expect actual
 '
 test_expect_success 'setup --prune-empty comparisons' '
-	git checkout --orphan master-no-a &&
+	git checkout --orphan main-no-a &&
 	git rm -rf . &&
 	unset test_tick &&
 	test_tick &&
@@ -343,7 +346,7 @@
 	mkdir dir &&
 	test_commit dir/D dir/D.t dir/D dir/Dx &&
 	test_commit E E.t E Ex &&
-	git checkout master-no-a &&
+	git checkout main-no-a &&
 	test_commit C C.t C Cx &&
 	git checkout branch-no-a &&
 	git merge Cx -m "Merge tag '\''C'\'' into branch" &&
@@ -397,7 +400,7 @@
 '
 
 test_expect_success '--remap-to-ancestor with filename filters' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard A &&
 	test_commit add-foo foo 1 &&
 	git branch moved-foo &&
@@ -407,15 +410,15 @@
 	git branch moved-bar &&
 	test_commit change-foo foo 2 &&
 	git filter-branch -f --remap-to-ancestor \
-		moved-foo moved-bar A..master \
+		moved-foo moved-bar A..main \
 		-- -- foo &&
 	test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
-	test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
+	test $(git rev-parse moved-foo) = $(git rev-parse main^) &&
 	test $orig_invariant = $(git rev-parse invariant)
 '
 
 test_expect_success 'automatic remapping to ancestor with filename filters' '
-	git checkout master &&
+	git checkout main &&
 	git reset --hard A &&
 	test_commit add-foo2 foo 1 &&
 	git branch moved-foo2 &&
@@ -425,10 +428,10 @@
 	git branch moved-bar2 &&
 	test_commit change-foo2 foo 2 &&
 	git filter-branch -f \
-		moved-foo2 moved-bar2 A..master \
+		moved-foo2 moved-bar2 A..main \
 		-- -- foo &&
 	test $(git rev-parse moved-foo2) = $(git rev-parse moved-bar2) &&
-	test $(git rev-parse moved-foo2) = $(git rev-parse master^) &&
+	test $(git rev-parse moved-foo2) = $(git rev-parse main^) &&
 	test $orig_invariant = $(git rev-parse invariant2)
 '
 
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 05f411c..2f72c5c 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -7,6 +7,9 @@
 
 Tests for operations with tags.'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
@@ -17,6 +20,13 @@
 	git show-ref --quiet --verify refs/tags/"$1"
 }
 
+test_expect_success 'setup' '
+	test_oid_cache <<-EOM
+	othersigheader sha1:gpgsig-sha256
+	othersigheader sha256:gpgsig
+	EOM
+'
+
 test_expect_success 'listing all tags in an empty tree should succeed' '
 	git tag -l &&
 	git tag
@@ -88,7 +98,7 @@
 	git tag --create-reflog tag_with_reflog &&
 	git reflog exists refs/tags/tag_with_reflog &&
 	sed -e "s/^.*	//" .git/logs/refs/tags/tag_with_reflog >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'annotated tag with --create-reflog has correct message' '
@@ -99,7 +109,7 @@
 	git tag -m "annotated tag" --create-reflog tag_with_reflog &&
 	git reflog exists refs/tags/tag_with_reflog &&
 	sed -e "s/^.*	//" .git/logs/refs/tags/tag_with_reflog >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success '--create-reflog does not create reflog on failure' '
@@ -1371,6 +1381,24 @@
 	'test_config gpg.program echo &&
 	 test_must_fail git tag -s -m tail tag-gpg-failure'
 
+# try to produce invalid signature
+test_expect_success GPG 'git verifies tag is valid with double signature' '
+	git tag -s -m tail tag-gpg-double-sig &&
+	git cat-file tag tag-gpg-double-sig >tag &&
+	othersigheader=$(test_oid othersigheader) &&
+	sed -ne "/^\$/q;p" tag >new-tag &&
+	cat <<-EOM >>new-tag &&
+	$othersigheader -----BEGIN PGP SIGNATURE-----
+	 someinvaliddata
+	 -----END PGP SIGNATURE-----
+	EOM
+	sed -e "1,/^tagger/d" tag >>new-tag &&
+	new_tag=$(git hash-object -t tag -w new-tag) &&
+	git update-ref refs/tags/tag-gpg-double-sig $new_tag &&
+	git verify-tag tag-gpg-double-sig &&
+	git fsck
+'
+
 # try to sign with bad user.signingkey
 test_expect_success GPGSM \
 	'git tag -s fails if gpgsm is misconfigured (bad key)' \
@@ -1406,7 +1434,7 @@
 	# check the first line --- should be empty
 	echo >first.expect &&
 	sed -e 1q <actual >first.actual &&
-	test_i18ncmp first.expect first.actual
+	test_cmp first.expect first.actual
 '
 
 test_expect_success \
@@ -1583,7 +1611,7 @@
 "
 
 test_expect_success 'merging original branch into this branch' '
-	git merge --strategy=ours master &&
+	git merge --strategy=ours main &&
         git tag v4.0
 '
 
@@ -1729,7 +1757,7 @@
 	hint: Disable this message with "git config advice.nestedTag false"
 	EOF
 	git tag -m nested nested annotated-v4.0 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'multiple --points-at are OR-ed together' '
@@ -1944,15 +1972,15 @@
 	i=1 &&
 	while test $i -lt 8000
 	do
-		echo "commit refs/heads/master
+		echo "commit refs/heads/main
 committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
 data <<EOF
 commit #$i
 EOF"
-		test $i = 1 && echo "from refs/heads/master^0"
+		test $i = 1 && echo "from refs/heads/main^0"
 		i=$(($i + 1))
 	done | git fast-import &&
-	git checkout master &&
+	git checkout main &&
 	git tag far-far-away HEAD^ &&
 	run_with_limited_stack git tag --contains HEAD >actual &&
 	test_must_be_empty actual &&
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index fdb450e..0e7cf75 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -656,4 +656,134 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup trace2' '
+	GIT_TRACE2_BRIEF=1 &&
+	export GIT_TRACE2_BRIEF
+'
+
+test_expect_success TTY 'git returns SIGPIPE on early pager exit' '
+	test_when_finished "rm pager-used trace.normal" &&
+	test_config core.pager ">pager-used; head -n 1; exit 0" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test_match_signal 13 "$OUT"
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:0 " child-exits &&
+	test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git returns SIGPIPE on early pager non-zero exit' '
+	test_when_finished "rm pager-used trace.normal" &&
+	test_config core.pager ">pager-used; head -n 1; exit 1" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test_match_signal 13 "$OUT"
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:1 " child-exits &&
+	test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
+	test_when_finished "rm pager-used trace.normal" &&
+	test_config core.pager "wc >pager-used; exit 1" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test "$OUT" -eq 0
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:1 " child-exits &&
+	test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git discards nonexisting pager without SIGPIPE' '
+	test_when_finished "rm pager-used trace.normal" &&
+	test_config core.pager "wc >pager-used; does-not-exist" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test "$OUT" -eq 0
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:127 " child-exits &&
+	test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git attempts to page to nonexisting pager command, gets SIGPIPE' '
+	test_when_finished "rm trace.normal" &&
+	test_config core.pager "does-not-exist" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test_match_signal 13 "$OUT"
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:-1 " child-exits
+'
+
+test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
+	test_when_finished "rm pager-used trace.normal" &&
+	test_config core.pager ">pager-used; test-tool sigchain" &&
+	GIT_TRACE2="$(pwd)/trace.normal" &&
+	export GIT_TRACE2 &&
+	test_when_finished "unset GIT_TRACE2" &&
+
+	if test_have_prereq !MINGW
+	then
+		OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+		test_match_signal 13 "$OUT"
+	else
+		test_terminal git log
+	fi &&
+
+	grep child_exit trace.normal >child-exits &&
+	test_line_count = 1 child-exits &&
+	grep " code:143 " child-exits &&
+	test_path_is_file pager-used
+'
+
 test_done
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index 37525ca..2685258 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -109,15 +109,6 @@
 	test -z "$(git ls-files -m)"
 '
 
-test_expect_success 'grep with skip-worktree file' '
-	git update-index --no-skip-worktree 1 &&
-	echo test > 1 &&
-	git update-index 1 &&
-	git update-index --skip-worktree 1 &&
-	rm 1 &&
-	test "$(git grep --no-ext-grep test)" = "1:test"
-'
-
 echo ":000000 100644 $ZERO_OID $EMPTY_BLOB A	1" > expected
 test_expect_success 'diff-index does not examine skip-worktree absent entries' '
 	setup_absent &&
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
index e5c6a03..f2a8e76 100755
--- a/t/t7012-skip-worktree-writing.sh
+++ b/t/t7012-skip-worktree-writing.sh
@@ -125,13 +125,13 @@
 test_expect_success 'git-clean, absent case' '
 	setup_absent &&
 	git clean -n > result &&
-	test_i18ncmp expected result
+	test_cmp expected result
 '
 
 test_expect_success 'git-clean, dirty case' '
 	setup_dirty &&
 	git clean -n > result &&
-	test_i18ncmp expected result
+	test_cmp expected result
 '
 
 test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 5c5bc32..3cefde9 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='signed tag tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -17,7 +20,7 @@
 	echo 3 >elif && git add elif &&
 	test_tick && git commit -m "third on side" &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick && git merge -S side &&
 	git tag -s -m merge merge &&
 
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index d521874..0f4344c 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -2,6 +2,9 @@
 
 test_description='basic work tree status reporting'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -51,11 +54,11 @@
 		git checkout -b side HEAD^ &&
 		git rm foo &&
 		git commit -m delete &&
-		test_must_fail git merge master &&
+		test_must_fail git merge main &&
 		test_must_fail git commit --dry-run >../actual &&
-		test_i18ncmp ../expect ../actual &&
+		test_cmp ../expect ../actual &&
 		git status >../actual &&
-		test_i18ncmp ../expect ../actual
+		test_cmp ../expect ../actual
 	)
 '
 
@@ -124,18 +127,18 @@
 
 test_expect_success 'status when conflicts with add and rm advice (deleted by them)' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 	test_commit init main.txt init &&
 	git checkout -b second_branch &&
 	git rm main.txt &&
 	git commit -m "main.txt deleted on second_branch" &&
 	test_commit second conflict.txt second &&
-	git checkout master &&
+	git checkout main &&
 	test_commit on_second main.txt on_second &&
-	test_commit master conflict.txt master &&
+	test_commit main conflict.txt main &&
 	test_must_fail git merge second_branch &&
 	cat >expected <<\EOF &&
-On branch master
+On branch main
 You have unmerged paths.
   (fix conflicts and run "git commit")
   (use "git merge --abort" to abort the merge)
@@ -148,7 +151,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -157,8 +160,8 @@
 	git checkout -b conflict &&
 	test_commit one main.txt one &&
 	git branch conflict_second &&
-	git mv main.txt sub_master.txt &&
-	git commit -m "main.txt renamed in sub_master.txt" &&
+	git mv main.txt sub_main.txt &&
+	git commit -m "main.txt renamed in sub_main.txt" &&
 	git checkout conflict_second &&
 	git mv main.txt sub_second.txt &&
 	git commit -m "main.txt renamed in sub_second.txt"
@@ -176,20 +179,20 @@
 Unmerged paths:
   (use "git add/rm <file>..." as appropriate to mark resolution)
 	both deleted:    main.txt
-	added by them:   sub_master.txt
+	added by them:   sub_main.txt
 	added by us:     sub_second.txt
 
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when conflicts with only rm advice (both deleted)' '
 	git reset --hard conflict_second &&
 	test_must_fail git merge conflict &&
-	git add sub_master.txt &&
+	git add sub_main.txt &&
 	git add sub_second.txt &&
 	cat >expected <<\EOF &&
 On branch conflict_second
@@ -198,7 +201,7 @@
   (use "git merge --abort" to abort the merge)
 
 Changes to be committed:
-	new file:   sub_master.txt
+	new file:   sub_main.txt
 
 Unmerged paths:
   (use "git rm <file>..." to mark resolution)
@@ -207,14 +210,14 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual &&
+	test_cmp expected actual &&
 	git reset --hard &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'status --branch with detached HEAD' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git status --branch --porcelain >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
@@ -224,13 +227,13 @@
 	?? expected
 	?? mdconflict/
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 ## Duplicate the above test and verify --porcelain=v1 arg parsing.
 test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
 	git reset --hard &&
-	git checkout master^0 &&
+	git checkout main^0 &&
 	git status --branch --porcelain=v1 >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
@@ -240,7 +243,7 @@
 	?? expected
 	?? mdconflict/
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 ## Verify parser error on invalid --porcelain argument.
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index a682a3d..accefde 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -2,6 +2,9 @@
 
 test_description='test untracked cache'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # On some filesystems (e.g. FreeBSD's ext2 and ufs) directory mtime
@@ -475,7 +478,7 @@
 test_expect_success 'set up sparse checkout' '
 	echo "done/[a-z]*" >.git/info/sparse-checkout &&
 	test_config core.sparsecheckout true &&
-	git checkout master &&
+	git checkout main &&
 	git update-index --force-untracked-cache &&
 	git status --porcelain >/dev/null && # prime the cache
 	test_path_is_missing done/.gitignore &&
@@ -728,19 +731,19 @@
 	cp -R done dthree dtwo four three ../other_worktree &&
 	GIT_WORK_TREE=../other_worktree git status 2>../err &&
 	echo "warning: untracked cache is disabled on this system or location" >../expect &&
-	test_i18ncmp ../expect ../err
+	test_cmp ../expect ../err
 '
 
 test_expect_success 'untracked cache survives a checkout' '
 	git commit --allow-empty -m empty &&
 	test-tool dump-untracked-cache >../before &&
-	test_when_finished  "git checkout master" &&
+	test_when_finished  "git checkout main" &&
 	git checkout -b other_branch &&
 	test-tool dump-untracked-cache >../after &&
 	test_cmp ../before ../after &&
 	test_commit test &&
 	test-tool dump-untracked-cache >../before &&
-	git checkout master &&
+	git checkout main &&
 	test-tool dump-untracked-cache >../after &&
 	test_cmp ../before ../after
 '
@@ -775,7 +778,7 @@
 	git checkout HEAD~ &&
 	status_is_clean &&
 	status_is_clean &&
-	git checkout master &&
+	git checkout main &&
 	avoid_racy &&
 	status_is_clean &&
 	status_is_clean
@@ -786,7 +789,7 @@
 	git checkout HEAD~ &&
 	status_is_clean &&
 	status_is_clean &&
-	git checkout master &&
+	git checkout main &&
 	avoid_racy &&
 	status_is_clean &&
 	status_is_clean
@@ -810,7 +813,7 @@
 	git checkout HEAD~ &&
 	status_is_clean &&
 	status_is_clean &&
-	git checkout master &&
+	git checkout main &&
 	avoid_racy &&
 	status_is_clean &&
 	test-tool dump-untracked-cache >../actual &&
@@ -828,7 +831,7 @@
 	git checkout HEAD~ &&
 	status_is_clean &&
 	status_is_clean &&
-	git checkout master &&
+	git checkout main &&
 	avoid_racy &&
 	status_is_clean &&
 	status_is_clean
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 601b478..4613882 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -5,6 +5,9 @@
 This test exercises porcelain V2 output for git status.'
 
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index b1affb0..601b2bf 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -7,6 +7,9 @@
 
 Documented tests for git reset'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 commit_msg () {
@@ -72,14 +75,14 @@
 	hex=$(git log -1 --format="%h") &&
 	git reset --hard >.actual &&
 	echo HEAD is now at $hex $(commit_msg) >.expected &&
-	test_i18ncmp .expected .actual
+	test_cmp .expected .actual
 '
 
 test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '
 	hex=$(git log -1 --format="%h") &&
 	git -c "i18n.logOutputEncoding=$test_encoding" reset --hard >.actual &&
 	echo HEAD is now at $hex $(commit_msg $test_encoding) >.expected &&
-	test_i18ncmp .expected .actual
+	test_cmp .expected .actual
 '
 
 test_expect_success 'giving a non existing revision should fail' '
@@ -145,7 +148,7 @@
 	printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
 	git commit -a -m "the change in branch2" &&
 
-	git checkout master &&
+	git checkout main &&
 	git branch -D branch1 branch2 &&
 	check_changes $head5
 '
@@ -167,7 +170,7 @@
 	printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
 	git commit -a -m "the line in branch3" &&
 
-	git checkout master &&
+	git checkout main &&
 	git branch -D branch3 branch4 &&
 	check_changes $head5
 '
@@ -380,7 +383,7 @@
 	git reset --hard ORIG_HEAD &&
 	check_changes $head5 &&
 
-	git checkout master &&
+	git checkout main &&
 	git branch -D branch1 branch2 &&
 	check_changes $head5
 '
@@ -466,7 +469,7 @@
 	EOF
 	echo 123 >>file2 &&
 	git reset --mixed HEAD >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'resetting specific path that is unmerged' '
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 16faa07..7948ec3 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -33,7 +33,7 @@
 
 '
 
-test_expect_success 'reset --hard did not corrupt index or cached-tree' '
+test_expect_success 'reset --hard did not corrupt index or cache-tree' '
 
 	T=$(git write-tree) &&
 	rm -f .git/index &&
diff --git a/t/t7113-post-index-change-hook.sh b/t/t7113-post-index-change-hook.sh
index f011ad7..688fa99 100755
--- a/t/t7113-post-index-change-hook.sh
+++ b/t/t7113-post-index-change-hook.sh
@@ -2,6 +2,9 @@
 
 test_description='post index change hook'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -85,7 +88,7 @@
 	EOF
 	: force index to be dirty &&
 	test-tool chmtime +60 dir1/file1.txt &&
-	git checkout master &&
+	git checkout main &&
 	test_path_is_file testsuccess && rm -f testsuccess &&
 	test_path_is_missing testfailure &&
 	test-tool chmtime +60 dir1/file1.txt &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index b36a930..7f6e23a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -5,10 +5,10 @@
 
 test_description='git checkout tests.
 
-Creates master, forks renamer and side branches from it.
+Creates main, forks renamer and side branches from it.
 Test switching across them.
 
-  ! [master] Initial A one, A two
+  ! [main] Initial A one, A two
    * [renamer] Renamer R one->uno, M two
     ! [side] Side M one, D two, A three
      ! [simple] Simple D one, M two
@@ -16,10 +16,13 @@
      + [simple] Simple D one, M two
     +  [side] Side M one, D two, A three
    *   [renamer] Renamer R one->uno, M two
-  +*++ [master] Initial A one, A two
+  +*++ [main] Initial A one, A two
 
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_tick
@@ -46,27 +49,27 @@
 	fill a b c d e f >two &&
 	git commit -a -m "Renamer R one->uno, M two" &&
 
-	git checkout -b side master &&
+	git checkout -b side main &&
 	fill 1 2 3 4 5 6 7 >one &&
 	fill A B C D E >three &&
 	rm -f two &&
 	git update-index --add --remove one two three &&
 	git commit -m "Side M one, D two, A three" &&
 
-	git checkout -b simple master &&
+	git checkout -b simple main &&
 	rm -f one &&
 	fill a c e >two &&
 	git commit -a -m "Simple D one, M two" &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'checkout from non-existing branch' '
-	git checkout -b delete-me master &&
+	git checkout -b delete-me main &&
 	git update-ref -d --no-deref refs/heads/delete-me &&
 	test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
-	git checkout master &&
-	test refs/heads/master = "$(git symbolic-ref HEAD)"
+	git checkout main &&
+	test refs/heads/main = "$(git symbolic-ref HEAD)"
 '
 
 test_expect_success 'checkout with dirty tree without -m' '
@@ -81,7 +84,7 @@
 '
 
 test_expect_success 'checkout with unrelated dirty tree without -m' '
-	git checkout -f master &&
+	git checkout -f main &&
 	fill 0 1 2 3 4 5 6 7 8 >same &&
 	cp same kept &&
 	git checkout side >messages &&
@@ -91,7 +94,7 @@
 '
 
 test_expect_success 'checkout -m with dirty tree' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git clean -f &&
 
 	fill 0 1 2 3 4 5 6 7 8 >one &&
@@ -102,9 +105,9 @@
 	printf "M\t%s\n" one >expect.messages &&
 	test_cmp expect.messages messages &&
 
-	fill "M	one" "A	three" "D	two" >expect.master &&
-	git diff --name-status master >current.master &&
-	test_cmp expect.master current.master &&
+	fill "M	one" "A	three" "D	two" >expect.main &&
+	git diff --name-status main >current.main &&
+	test_cmp expect.main current.main &&
 
 	fill "M	one" >expect.side &&
 	git diff --name-status side >current.side &&
@@ -115,7 +118,7 @@
 '
 
 test_expect_success 'checkout -m with dirty tree, renamed' '
-	git checkout -f master && git clean -f &&
+	git checkout -f main && git clean -f &&
 
 	fill 1 2 3 4 5 7 8 >one &&
 	if git checkout renamer
@@ -135,7 +138,7 @@
 '
 
 test_expect_success 'checkout -m with merge conflict' '
-	git checkout -f master && git clean -f &&
+	git checkout -f main && git clean -f &&
 
 	fill 1 T 3 4 5 6 S 8 >one &&
 	if git checkout renamer
@@ -148,7 +151,7 @@
 
 	git checkout -m renamer &&
 
-	git diff master:one :3:uno |
+	git diff main:one :3:uno |
 	sed -e "1,/^@@/d" -e "/^ /d" -e "s/^-/d/" -e "s/^+/a/" >current &&
 	fill d2 aT d7 aS >expect &&
 	test_cmp expect current &&
@@ -157,7 +160,7 @@
 '
 
 test_expect_success 'format of merge conflict from checkout -m' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git clean -f &&
 
 	fill b d >two &&
@@ -181,7 +184,7 @@
 '
 
 test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard &&
 	git clean -f &&
 
@@ -193,7 +196,7 @@
 	a
 	c
 	e
-	||||||| master
+	||||||| main
 	a
 	b
 	c
@@ -208,7 +211,7 @@
 '
 
 test_expect_success 'switch to another branch while carrying a deletion' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git reset --hard &&
 	git clean -f &&
 	git rm two &&
@@ -229,7 +232,7 @@
 	test_i18ngrep "HEAD is now at $rev" messages &&
 	test_line_count = 1 messages &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	if git symbolic-ref HEAD >/dev/null 2>&1
 	then
@@ -245,11 +248,11 @@
 	rev=$(git rev-parse --short renamer^) &&
 	git checkout -f renamer &&
 	git clean -f &&
-	GIT_TEST_GETTEXT_POISON=false git checkout renamer^ 2>messages &&
+	git checkout renamer^ 2>messages &&
 	grep "HEAD is now at $rev" messages &&
 	test_line_count -gt 1 messages &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	if git symbolic-ref HEAD >/dev/null 2>&1
 	then
@@ -261,11 +264,11 @@
 '
 
 test_expect_success 'checkout to detach HEAD with branchname^' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git clean -f &&
 	git checkout renamer^ &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	if git symbolic-ref HEAD >/dev/null 2>&1
 	then
@@ -277,11 +280,11 @@
 '
 
 test_expect_success 'checkout to detach HEAD with :/message' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git clean -f &&
 	git checkout ":/Initial" &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	if git symbolic-ref HEAD >/dev/null 2>&1
 	then
@@ -293,11 +296,11 @@
 '
 
 test_expect_success 'checkout to detach HEAD with HEAD^0' '
-	git checkout -f master &&
+	git checkout -f main &&
 	git clean -f &&
 	git checkout HEAD^0 &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	if git symbolic-ref HEAD >/dev/null 2>&1
 	then
@@ -310,13 +313,13 @@
 
 test_expect_success 'checkout with ambiguous tag/branch names' '
 	git tag both side &&
-	git branch both master &&
+	git branch both main &&
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	git checkout both &&
 	H=$(git rev-parse --verify HEAD) &&
-	M=$(git show-ref -s --verify refs/heads/master) &&
+	M=$(git show-ref -s --verify refs/heads/main) &&
 	test "z$H" = "z$M" &&
 	name=$(git symbolic-ref HEAD 2>/dev/null) &&
 	test "z$name" = zrefs/heads/both
@@ -324,12 +327,12 @@
 
 test_expect_success 'checkout with ambiguous tag/branch names' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	git tag frotz side &&
-	git branch frotz master &&
+	git branch frotz main &&
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	git checkout tags/frotz &&
 	H=$(git rev-parse --verify HEAD) &&
@@ -346,7 +349,7 @@
 
 test_expect_success 'switch branches while in subdirectory' '
 	git reset --hard &&
-	git checkout master &&
+	git checkout main &&
 
 	mkdir subs &&
 	git -C subs checkout side &&
@@ -362,7 +365,7 @@
 	git add subs/bero &&
 	git commit -m "add subs/bero" &&
 
-	git checkout master &&
+	git checkout main &&
 	mkdir -p subs &&
 	git -C subs checkout side -- bero &&
 	test -f subs/bero
@@ -370,7 +373,7 @@
 
 test_expect_success 'checkout w/--track sets up tracking' '
     git config branch.autosetupmerge false &&
-    git checkout master &&
+    git checkout main &&
     git checkout --track -b track1 &&
     test "$(git config branch.track1.remote)" &&
     test "$(git config branch.track1.merge)"
@@ -379,40 +382,40 @@
 test_expect_success 'checkout w/autosetupmerge=always sets up tracking' '
     test_when_finished git config branch.autosetupmerge false &&
     git config branch.autosetupmerge always &&
-    git checkout master &&
+    git checkout main &&
     git checkout -b track2 &&
     test "$(git config branch.track2.remote)" &&
     test "$(git config branch.track2.merge)"
 '
 
 test_expect_success 'checkout w/--track from non-branch HEAD fails' '
-    git checkout master^0 &&
+    git checkout main^0 &&
     test_must_fail git symbolic-ref HEAD &&
     test_must_fail git checkout --track -b track &&
     test_must_fail git rev-parse --verify track &&
     test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'checkout w/--track from tag fails' '
-    git checkout master^0 &&
+    git checkout main^0 &&
     test_must_fail git symbolic-ref HEAD &&
     test_must_fail git checkout --track -b track frotz &&
     test_must_fail git rev-parse --verify track &&
     test_must_fail git symbolic-ref HEAD &&
-    test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+    test "z$(git rev-parse main^0)" = "z$(git rev-parse HEAD)"
 '
 
 test_expect_success 'detach a symbolic link HEAD' '
-    git checkout master &&
+    git checkout main &&
     git config --bool core.prefersymlinkrefs yes &&
     git checkout side &&
-    git checkout master &&
+    git checkout main &&
     it=$(git symbolic-ref HEAD) &&
-    test "z$it" = zrefs/heads/master &&
-    here=$(git rev-parse --verify refs/heads/master) &&
+    test "z$it" = zrefs/heads/main &&
+    here=$(git rev-parse --verify refs/heads/main) &&
     git checkout side^ &&
-    test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
+    test "z$(git rev-parse --verify refs/heads/main)" = "z$here"
 '
 
 test_expect_success 'checkout with --track fakes a sensible -b <name>' '
@@ -423,13 +426,13 @@
     test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
     test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout master && git branch -D koala/bear &&
+    git checkout main && git branch -D koala/bear &&
 
     git checkout --track refs/remotes/origin/koala/bear &&
     test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
     test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
 
-    git checkout master && git branch -D koala/bear &&
+    git checkout main && git branch -D koala/bear &&
 
     git checkout --track remotes/origin/koala/bear &&
     test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
@@ -582,17 +585,17 @@
 '
 
 test_expect_success 'failing checkout -b should not break working tree' '
-	git reset --hard master &&
-	git symbolic-ref HEAD refs/heads/master &&
+	git reset --hard main &&
+	git symbolic-ref HEAD refs/heads/main &&
 	test_must_fail git checkout -b renamer side^ &&
-	test $(git symbolic-ref HEAD) = refs/heads/master &&
+	test $(git symbolic-ref HEAD) = refs/heads/main &&
 	git diff --exit-code &&
 	git diff --cached --exit-code
 '
 
 test_expect_success 'switch out of non-branch' '
-	git reset --hard master &&
-	git checkout master^0 &&
+	git reset --hard main &&
+	git checkout main^0 &&
 	echo modified >one &&
 	test_must_fail git checkout renamer 2>error.log &&
 	! grep "^Previous HEAD" error.log
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index cb5e34d..a74816c 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -110,7 +110,7 @@
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
+test_expect_success 'git clean with relative prefix' '
 
 	mkdir -p build docs &&
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -123,7 +123,7 @@
 	verbose test "$would_clean" = ../src/part3.c
 '
 
-test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
+test_expect_success 'git clean with absolute path' '
 
 	mkdir -p build docs &&
 	touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -407,7 +407,7 @@
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '
+test_expect_success 'core.excludesfile' '
 
 	echo excludes >excludes &&
 	echo included >included &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index fec7e02..a924fdb 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -9,6 +9,9 @@
 subcommands of git submodule.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'submodule deinit works on empty repository' '
@@ -52,7 +55,7 @@
 	EOF
 	git init repo-no-commits &&
 	test_must_fail git submodule add ../a ./repo-no-commits 2>actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'status should ignore inner git repo when not added' '
@@ -124,7 +127,7 @@
 }
 
 test_expect_success 'submodule add' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -182,7 +185,7 @@
 		git add --force .gitignore &&
 		git commit -m"Ignore everything" &&
 		! git submodule add "$submodurl" submod >actual 2>&1 &&
-		test_i18ncmp expect actual
+		test_cmp expect actual
 	)
 '
 
@@ -223,7 +226,7 @@
 	echo "refs/heads/initial" >expect-head &&
 	cat <<-\EOF >expect-heads &&
 	refs/heads/initial
-	refs/heads/master
+	refs/heads/main
 	EOF
 
 	(
@@ -241,7 +244,7 @@
 '
 
 test_expect_success 'submodule add with ./ in path' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -257,7 +260,7 @@
 '
 
 test_expect_success 'submodule add with /././ in path' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -273,7 +276,7 @@
 '
 
 test_expect_success 'submodule add with // in path' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -289,7 +292,7 @@
 '
 
 test_expect_success 'submodule add with /.. in path' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -305,7 +308,7 @@
 '
 
 test_expect_success 'submodule add with ./, /.. and // in path' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	(
 		cd addtest &&
@@ -335,7 +338,7 @@
 '
 
 test_expect_success 'submodule add in subdirectory' '
-	echo "refs/heads/master" >expect &&
+	echo "refs/heads/main" >expect &&
 
 	mkdir addtest/sub &&
 	(
@@ -557,7 +560,7 @@
 
 test_expect_success 'checkout superproject with subproject already present' '
 	git checkout initial &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'apply submodule diff' '
@@ -574,7 +577,7 @@
 	git checkout second &&
 	git apply --index P.diff &&
 
-	git diff --cached master >staged &&
+	git diff --cached main >staged &&
 	test_must_be_empty staged
 '
 
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 7608814..9c3cc4c 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -190,7 +190,7 @@
 	  < Add foo5
 
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
@@ -200,7 +200,7 @@
 	  > Add foo5
 
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 rm -rf sm1 &&
@@ -211,7 +211,7 @@
 	* sm1 $head4(submodule)->$head5(blob):
 
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 rm -f sm1 &&
@@ -224,7 +224,7 @@
 	  Warn: sm1 doesn't contain commit $head4_full
 
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 commit_file
@@ -235,7 +235,7 @@
 	  > Add foo7
 
 	EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -292,7 +292,7 @@
 
 test_expect_success '--for-status' "
 	git submodule summary --for-status HEAD^ >actual &&
-	test_i18ncmp - actual <<-EOF
+	test_cmp - actual <<-EOF
 	* sm1 $head6...0000000:
 
 	* sm2 0000000...$head7 (2):
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 0726799..7d2ac33 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -8,6 +8,9 @@
 These tests exercise the "git submodule sync" subcommand.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -78,7 +81,7 @@
 	(
 		cd super &&
 		cd submodule &&
-		git checkout master &&
+		git checkout main &&
 		git pull
 	) &&
 	mv submodule moved-submodule &&
@@ -112,7 +115,7 @@
 	)" &&
 	(
 		cd super-clone/submodule &&
-		git checkout master &&
+		git checkout main &&
 		git pull
 	) &&
 	(
@@ -140,7 +143,7 @@
 	)" &&
 	(
 		cd super-clone/submodule/sub-submodule &&
-		git checkout master &&
+		git checkout main &&
 		git pull
 	)
 '
@@ -168,7 +171,7 @@
 	)" &&
 	(
 		cd super-clone/submodule &&
-		git checkout master &&
+		git checkout main &&
 		git pull
 	) &&
 	(
@@ -199,7 +202,7 @@
 	)" &&
 	(
 		cd super-clone/submodule/sub-submodule &&
-		git checkout master &&
+		git checkout main &&
 		git pull
 	)
 '
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index acb8766..ff3ba54 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -9,15 +9,18 @@
 submodule and "git submodule update --rebase/--merge" does not detach the HEAD.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
 compare_head()
 {
-    sha_master=$(git rev-list --max-count=1 master)
+    sha_main=$(git rev-list --max-count=1 main)
     sha_head=$(git rev-list --max-count=1 HEAD)
 
-    test "$sha_master" = "$sha_head"
+    test "$sha_main" = "$sha_head"
 }
 
 
@@ -152,9 +155,9 @@
 	 cd tmp &&
 	 git submodule update --init --recursive ../super >../../actual 2>../../actual2
 	) &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	sort actual2 >actual2.sorted &&
-	test_i18ncmp expect2 actual2.sorted
+	test_cmp expect2 actual2.sorted
 '
 
 cat <<EOF >expect2
@@ -171,7 +174,7 @@
 	  git submodule update --init sub 2>../../actual2
 	 )
 	) &&
-	test_i18ncmp expect2 actual2
+	test_cmp expect2 actual2
 '
 
 test_expect_success 'submodule update does not fetch already present commits' '
@@ -189,7 +192,7 @@
 	(cd super &&
 	  git submodule update > ../actual 2> ../actual.err
 	) &&
-	test_i18ncmp expected actual &&
+	test_cmp expected actual &&
 	test_must_be_empty actual.err
 '
 
@@ -262,14 +265,14 @@
 		cd super &&
 		git submodule update --remote --force submodule &&
 		git -C submodule log -1 --oneline >actual &&
-		git -C ../submodule log -1 --oneline master >expect &&
+		git -C ../submodule log -1 --oneline main >expect &&
 		test_cmp expect actual &&
 		git checkout -b test-branch &&
 		git submodule update --remote --force submodule &&
 		git -C submodule log -1 --oneline >actual &&
 		git -C ../submodule log -1 --oneline test-branch >expect &&
 		test_cmp expect actual &&
-		git checkout master &&
+		git checkout main &&
 		git branch -d test-branch &&
 		git reset --hard HEAD^
 	)
@@ -282,7 +285,7 @@
 	 git add file &&
 	 test_tick &&
 	 git commit -m "upstream line5" &&
-	 git checkout master
+	 git checkout main
 	) &&
 	(cd super &&
 	 git config submodule.submodule.branch test-branch &&
@@ -292,9 +295,9 @@
 	)
 '
 
-test_expect_success 'submodule update --rebase staying on master' '
+test_expect_success 'submodule update --rebase staying on main' '
 	(cd super/submodule &&
-	  git checkout master
+	  git checkout main
 	) &&
 	(cd super &&
 	 (cd submodule &&
@@ -306,7 +309,7 @@
 	)
 '
 
-test_expect_success 'submodule update --merge staying on master' '
+test_expect_success 'submodule update --merge staying on main' '
 	(cd super/submodule &&
 	  git reset --hard HEAD~1
 	) &&
@@ -458,7 +461,7 @@
 	(cd super &&
 	 test_must_fail git submodule update submodule 2>../actual
 	) &&
-	test_i18ncmp actual expect
+	test_cmp actual expect
 '
 
 cat << EOF >expect
@@ -476,7 +479,7 @@
 	 mkdir tmp && cd tmp &&
 	 test_must_fail git submodule update ../submodule 2>../../actual
 	) &&
-	test_i18ncmp actual expect
+	test_cmp actual expect
 '
 
 test_expect_success 'submodule update - command run for initial population of submodule' '
@@ -485,7 +488,7 @@
 	EOF
 	rm -rf super/submodule &&
 	test_must_fail git -C super submodule update 2>actual &&
-	test_i18ncmp expect actual &&
+	test_cmp expect actual &&
 	git -C super submodule update --checkout
 '
 
@@ -506,7 +509,7 @@
 	 mkdir -p tmp && cd tmp &&
 	 test_must_fail git submodule update --recursive ../super 2>../../actual
 	) &&
-	test_i18ncmp actual expect
+	test_cmp actual expect
 '
 
 test_expect_success 'submodule init does not copy command into .git/config' '
@@ -609,7 +612,7 @@
 	(cd super &&
 	 git config submodule.submodule.update none &&
 	 (cd submodule &&
-	  git checkout master &&
+	  git checkout main &&
 	  compare_head
 	 ) &&
 	 git diff --name-only >out &&
@@ -629,7 +632,7 @@
 	(cd super &&
 	 git config submodule.submodule.update none &&
 	 (cd submodule &&
-	  git checkout master &&
+	  git checkout main &&
 	  compare_head
 	 ) &&
 	 git diff --name-only >out &&
@@ -689,7 +692,7 @@
 test_expect_success 'submodule update continues after recursive checkout error' '
 	(cd super &&
 	 git reset --hard HEAD &&
-	 git checkout master &&
+	 git checkout main &&
 	 git submodule update &&
 	 (cd submodule &&
 	  git submodule add ../submodule subsubmodule &&
@@ -733,7 +736,7 @@
 
 test_expect_success 'submodule update exit immediately in case of merge conflict' '
 	(cd super &&
-	 git checkout master &&
+	 git checkout main &&
 	 git reset --hard HEAD &&
 	 (cd submodule &&
 	  (cd subsubmodule &&
@@ -751,7 +754,7 @@
 	 git add submodule2 &&
 	 git commit -m "two_new_submodule_commits" &&
 	 (cd submodule &&
-	  git checkout master &&
+	  git checkout main &&
 	  test_commit "conflict" file &&
 	  echo "conflict" > file
 	 ) &&
@@ -770,7 +773,7 @@
 
 test_expect_success 'submodule update exit immediately after recursive rebase error' '
 	(cd super &&
-	 git checkout master &&
+	 git checkout main &&
 	 git reset --hard HEAD &&
 	 (cd submodule &&
 	  git reset --hard HEAD &&
@@ -786,7 +789,7 @@
 	 git add submodule2 &&
 	 git commit -m "two_new_submodule_commits" &&
 	 (cd submodule &&
-	  git checkout master &&
+	  git checkout main &&
 	  test_commit "conflict2" file &&
 	  echo "conflict" > file
 	 ) &&
@@ -878,21 +881,21 @@
 	git clone subsubsuper_update_r subsubsuper_update_r2 &&
 	(cd subsubsuper_update_r2 &&
 	 test_commit "update_subsubsuper" file &&
-	 git push origin master
+	 git push origin main
 	) &&
 	git clone subsuper_update_r subsuper_update_r2 &&
 	(cd subsuper_update_r2 &&
 	 test_commit "update_subsuper" file &&
 	 git submodule add ../subsubsuper_update_r subsubmodule &&
 	 git commit -am "subsubmodule" &&
-	 git push origin master
+	 git push origin main
 	) &&
 	git clone super_update_r super_update_r2 &&
 	(cd super_update_r2 &&
 	 test_commit "update_super" file &&
 	 git submodule add ../subsuper_update_r submodule &&
 	 git commit -am "submodule" &&
-	 git push origin master
+	 git push origin main
 	) &&
 	rm -rf super_update_r2 &&
 	git clone super_update_r super_update_r2 &&
@@ -911,7 +914,7 @@
 
 test_expect_success 'submodule add properly re-creates deeper level submodules' '
 	(cd super &&
-	 git reset --hard master &&
+	 git reset --hard main &&
 	 rm -rf deeper/ &&
 	 git submodule add --force ../submodule deeper/submodule
 	)
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 6b2aa91..e2f110b 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -9,6 +9,9 @@
 that are currently checked out.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
@@ -77,7 +80,7 @@
 		git config foo.bar zar &&
 		git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
 	) &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat >expect <<EOF
@@ -93,7 +96,7 @@
 		cd clone/sub &&
 		git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual
 	) &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'setup nested submodules' '
@@ -174,7 +177,7 @@
 		cd clone2 &&
 		git submodule foreach --recursive "true" > ../actual
 	) &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat > expect <<EOF
@@ -194,7 +197,7 @@
 		cd untracked &&
 		git submodule foreach --recursive >../../actual
 	) &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 sub1sha1=$(cd clone2/sub1 && git rev-parse HEAD)
 sub2sha1=$(cd clone2/sub2 && git rev-parse HEAD)
@@ -226,7 +229,7 @@
 		cd clone2/untracked &&
 		git submodule foreach --recursive "echo toplevel: \$toplevel name: \$name path: \$sm_path displaypath: \$displaypath hash: \$sha1" >../../actual
 	) &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 cat > expect <<EOF
@@ -277,13 +280,13 @@
 sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
 
 cat > expect <<EOF
- $nested1sha1 nested1 (heads/master)
- $nested2sha1 nested1/nested2 (heads/master)
- $nested3sha1 nested1/nested2/nested3 (heads/master)
- $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $nested1sha1 nested1 (heads/main)
+ $nested2sha1 nested1/nested2 (heads/main)
+ $nested3sha1 nested1/nested2/nested3 (heads/main)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/main)
  $sub1sha1 sub1 ($sub1sha1_short)
  $sub2sha1 sub2 ($sub2sha1_short)
- $sub3sha1 sub3 (heads/master)
+ $sub3sha1 sub3 (heads/main)
 EOF
 
 test_expect_success 'test "status --recursive"' '
@@ -295,10 +298,10 @@
 '
 
 cat > expect <<EOF
- $nested1sha1 nested1 (heads/master)
+ $nested1sha1 nested1 (heads/main)
 +$nested2sha1 nested1/nested2 (file2~1)
- $nested3sha1 nested1/nested2/nested3 (heads/master)
- $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/main)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/main)
 EOF
 
 test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' '
@@ -316,13 +319,13 @@
 nested2sha1=$(git -C clone3/nested1/nested2 rev-parse HEAD)
 
 cat > expect <<EOF
- $nested1sha1 ../nested1 (heads/master)
+ $nested1sha1 ../nested1 (heads/main)
 +$nested2sha1 ../nested1/nested2 (file2)
- $nested3sha1 ../nested1/nested2/nested3 (heads/master)
- $submodulesha1 ../nested1/nested2/nested3/submodule (heads/master)
+ $nested3sha1 ../nested1/nested2/nested3 (heads/main)
+ $submodulesha1 ../nested1/nested2/nested3/submodule (heads/main)
  $sub1sha1 ../sub1 ($sub1sha1_short)
  $sub2sha1 ../sub2 ($sub2sha1_short)
- $sub3sha1 ../sub3 (heads/master)
+ $sub3sha1 ../sub3 (heads/main)
 EOF
 
 test_expect_success 'test "status --recursive" from sub directory' '
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index fc018e3..e17ac81 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -10,6 +10,9 @@
 '
 
 TEST_NO_CREATE_REPO=1
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'submodule on detached working tree' '
@@ -35,7 +38,7 @@
 			git rev-parse --verify HEAD >actual &&
 			test_cmp ../../../../expect actual
 		) &&
-		git push origin master
+		git push origin main
 	) &&
 	mkdir home2 &&
 	(
@@ -44,7 +47,7 @@
 		GIT_WORK_TREE="$(pwd)" &&
 		GIT_DIR="$(pwd)/.dotfiles" &&
 		export GIT_WORK_TREE GIT_DIR &&
-		git checkout master &&
+		git checkout main &&
 		git submodule update --init &&
 		(
 			unset GIT_WORK_TREE GIT_DIR &&
@@ -64,10 +67,10 @@
 		git clone --bare ../remote "$GIT_DIR" &&
 		git config core.bare false &&
 		git config core.worktree .. &&
-		git checkout master &&
+		git checkout main &&
 		git submodule add ../bundle1 .vim/bundle/dupe &&
 		test_commit "dupe" &&
-		git push origin master
+		git push origin main
 	) &&
 	(
 		cd home &&
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index f7e7e94..f0f6b9f 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='check handling of .gitmodule path with dash'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'create submodule with dash in path' '
@@ -36,7 +39,7 @@
 	sed "s/sub/sub /" <tree >tree.new &&
 	tree=$(git -C super mktree <tree.new) &&
 	commit=$(echo with space | git -C super commit-tree $tree) &&
-	git -C super update-ref refs/heads/master $commit &&
+	git -C super update-ref refs/heads/main $commit &&
 
 	test_must_fail git clone --recurse-submodules super dst 2>err &&
 	test_i18ngrep "sub " err
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 6d19ece..e41ac18 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -15,7 +15,7 @@
 
 	printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
 	printf "%s" "$1" >"$expect" &&
-	test_i18ncmp "$expect" "$actual"
+	test_cmp "$expect" "$actual"
 }
 
 # A sanity check to see if commit is working at all.
@@ -356,7 +356,7 @@
 	touch commit-template-check &&
 	git add commit-template-check &&
 	GIT_EDITOR="cat >editor-input" git commit --untracked-files=no --allow-empty-message &&
-	test_i18ncmp expected-template editor-input
+	test_cmp expected-template editor-input
 '
 
 test_expect_success 'setup empty commit with unstaged rename and copy' '
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index 110b4bf..512ae27 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -7,8 +7,11 @@
 # signoff
 
 test_description='git commit'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
-. "$TEST_DIRECTORY/diff-lib.sh"
+. "$TEST_DIRECTORY/lib-diff.sh"
 
 author='The Real Author <someguy@his.email.org>'
 
@@ -603,7 +606,7 @@
 	echo zero >zero &&
 	git add zero &&
 	git commit -m "add zero" &&
-	git checkout master &&
+	git checkout main &&
 
 	git merge -s ours side -m "empty ok" &&
 	git diff HEAD^ HEAD >actual &&
diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh
index 14c92e4..6396897 100755
--- a/t/t7502-commit-porcelain.sh
+++ b/t/t7502-commit-porcelain.sh
@@ -2,6 +2,9 @@
 
 test_description='git commit porcelain-ish'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 commit_msg_is () {
@@ -10,7 +13,7 @@
 
 	printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual &&
 	printf "%s" "$1" >$expect &&
-	test_i18ncmp $expect $actual
+	test_cmp $expect $actual
 }
 
 # Arguments: [<prefix] [<commit message>] [<commit options>]
@@ -32,7 +35,7 @@
 	SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')"
 	echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp &&
 
-	test_i18ncmp exp act
+	test_cmp exp act
 }
 
 test_expect_success 'output summary format' '
@@ -76,7 +79,7 @@
 
 output_tests_cleanup() {
 	# this is needed for "do not fire editor in the presence of conflicts"
-	git checkout master &&
+	git checkout main &&
 
 	# this is needed for the "partial removal" test to pass
 	git rm file1 &&
@@ -297,7 +300,7 @@
 # with '#' will be ignored, and an empty message aborts the commit." >expect
 
 test_expect_success 'cleanup commit messages (strip option,-F,-e): output' '
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' '
@@ -440,7 +443,7 @@
 	git add g &&
 	git commit -m "add g" &&
 	git branch second &&
-	echo master >g &&
+	echo main >g &&
 	echo g >h &&
 	git add g h &&
 	git commit -m "modify g and add h" &&
@@ -449,7 +452,7 @@
 	git add g &&
 	git commit -m second &&
 	# Must fail due to conflict
-	test_must_fail git cherry-pick -n master &&
+	test_must_fail git cherry-pick -n main &&
 	echo "editor not started" >.git/result &&
 	(
 		GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" &&
@@ -478,9 +481,9 @@
 
 test_expect_success 'Hand committing of a redundant merge removes dups' '
 
-	git rev-parse second master >expect &&
-	test_must_fail git merge second master &&
-	git checkout master g &&
+	git rev-parse second main >expect &&
+	test_must_fail git merge second main &&
+	git checkout main g &&
 	EDITOR=: git commit -a &&
 	git cat-file commit HEAD >raw &&
 	sed -n -e "s/^parent //p" -e "/^$/q" raw >actual &&
diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
index b348545..606d8d0 100755
--- a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
+++ b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
@@ -2,6 +2,9 @@
 
 test_description='pre-commit and pre-merge-commit hooks'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 HOOKDIR="$(git rev-parse --git-dir)/hooks"
@@ -44,16 +47,16 @@
 	git add foo &&
 	git commit -m "make it non-ff" &&
 	git branch side-orig side &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'setup conflicting branches' '
-	test_when_finished "git checkout master" &&
-	git checkout -b conflicting-a master &&
+	test_when_finished "git checkout main" &&
+	git checkout -b conflicting-a main &&
 	echo a >conflicting &&
 	git add conflicting &&
 	git commit -m conflicting-a &&
-	git checkout -b conflicting-b master &&
+	git checkout -b conflicting-b main &&
 	echo b >conflicting &&
 	git add conflicting &&
 	git commit -m conflicting-b
@@ -71,8 +74,8 @@
 	test_when_finished "rm -f actual_hooks" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge -m "merge master" master &&
-	git checkout master &&
+	git merge -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
@@ -88,8 +91,8 @@
 	test_when_finished "rm -f actual_hooks" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge --no-verify -m "merge master" master &&
-	git checkout master &&
+	git merge --no-verify -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
@@ -108,15 +111,15 @@
 	cp "$HOOKDIR/success.sample" "$PREMERGE" &&
 	echo "$PREMERGE" >expected_hooks &&
 	git checkout side &&
-	git merge -m "merge master" master &&
-	git checkout master &&
+	git merge -m "merge main" main &&
+	git checkout main &&
 	test_cmp expected_hooks actual_hooks
 '
 
 test_expect_success 'automatic merge fails; both hooks are available' '
 	test_when_finished "rm -f \"$PREMERGE\" \"$PRECOMMIT\"" &&
 	test_when_finished "rm -f expected_hooks actual_hooks" &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	cp "$HOOKDIR/success.sample" "$PREMERGE" &&
 	cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
 
@@ -145,8 +148,8 @@
 	cp "$HOOKDIR/success.sample" "$PREMERGE" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge --no-verify -m "merge master" master &&
-	git checkout master &&
+	git merge --no-verify -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
@@ -174,8 +177,8 @@
 	cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
 	echo "$PREMERGE" >expected_hooks &&
 	git checkout side &&
-	test_must_fail git merge -m "merge master" master &&
-	git checkout master &&
+	test_must_fail git merge -m "merge main" main &&
+	git checkout main &&
 	test_cmp expected_hooks actual_hooks
 '
 
@@ -184,8 +187,8 @@
 	cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge --no-verify -m "merge master" master &&
-	git checkout master &&
+	git merge --no-verify -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
@@ -212,8 +215,8 @@
 	cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge -m "merge master" master &&
-	git checkout master &&
+	git merge -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
@@ -222,8 +225,8 @@
 	cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
 	git branch -f side side-orig &&
 	git checkout side &&
-	git merge --no-verify -m "merge master" master &&
-	git checkout master &&
+	git merge --no-verify -m "merge main" main &&
+	git checkout main &&
 	test_path_is_missing actual_hooks
 '
 
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
index 31b9c6a..4e75925 100755
--- a/t/t7504-commit-msg-hook.sh
+++ b/t/t7504-commit-msg-hook.sh
@@ -2,6 +2,9 @@
 
 test_description='commit-msg hook'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'with no hook' '
@@ -142,12 +145,12 @@
 test_expect_success 'merge fails with failing hook' '
 
 	test_when_finished "git branch -D newbranch" &&
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout --orphan newbranch &&
 	: >file2 &&
 	git add file2 &&
 	git commit --no-verify file2 -m in-side-branch &&
-	test_must_fail git merge --allow-unrelated-histories master &&
+	test_must_fail git merge --allow-unrelated-histories main &&
 	commit_msg_is "in-side-branch" # HEAD before merge
 
 '
@@ -155,14 +158,14 @@
 test_expect_success 'merge bypasses failing hook with --no-verify' '
 
 	test_when_finished "git branch -D newbranch" &&
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout --orphan newbranch &&
 	git rm -f file &&
 	: >file2 &&
 	git add file2 &&
 	git commit --no-verify file2 -m in-side-branch &&
-	git merge --no-verify --allow-unrelated-histories master &&
-	commit_msg_is "Merge branch '\''master'\'' into newbranch"
+	git merge --no-verify --allow-unrelated-histories main &&
+	commit_msg_is "Merge branch '\''main'\'' into newbranch"
 '
 
 
@@ -248,28 +251,28 @@
 
 test_expect_success 'hook called in git-merge picks up commit message' '
 	test_when_finished "git branch -D newbranch" &&
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout --orphan newbranch &&
 	git rm -f file &&
 	: >file2 &&
 	git add file2 &&
 	git commit --no-verify file2 -m in-side-branch &&
-	git merge --allow-unrelated-histories master &&
+	git merge --allow-unrelated-histories main &&
 	commit_msg_is "new message"
 '
 
 test_expect_failure 'merge --continue remembers --no-verify' '
 	test_when_finished "git branch -D newbranch" &&
-	test_when_finished "git checkout -f master" &&
-	git checkout master &&
+	test_when_finished "git checkout -f main" &&
+	git checkout main &&
 	echo a >file2 &&
 	git add file2 &&
-	git commit --no-verify -m "add file2 to master" &&
-	git checkout -b newbranch master^ &&
+	git commit --no-verify -m "add file2 to main" &&
+	git checkout -b newbranch main^ &&
 	echo b >file2 &&
 	git add file2 &&
 	git commit --no-verify file2 -m in-side-branch &&
-	git merge --no-verify -m not-rewritten-by-hook master &&
+	git merge --no-verify -m not-rewritten-by-hook main &&
 	# resolve conflict:
 	echo c >file2 &&
 	git add file2 &&
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
index 94f85cd..7a8194c 100755
--- a/t/t7505-prepare-commit-msg-hook.sh
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -2,6 +2,9 @@
 
 test_description='prepare-commit-msg hook'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'set up commits for rebasing' '
@@ -15,7 +18,7 @@
 	do
 		test_commit rebase-$i c $i
 	done &&
-	git checkout master &&
+	git checkout main &&
 
 	cat >rebase-todo <<-EOF
 	pick $(git rev-parse rebase-a)
@@ -190,7 +193,7 @@
 
 test_expect_success 'with hook (merge)' '
 
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other HEAD@{1} &&
 	echo "more" >>file &&
 	git add file &&
@@ -202,7 +205,7 @@
 
 test_expect_success 'with hook and editor (merge)' '
 
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other HEAD@{1} &&
 	echo "more" >>file &&
 	git add file &&
@@ -215,10 +218,10 @@
 test_rebase () {
 	expect=$1 &&
 	mode=$2 &&
-	test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase ${mode:--i})" '
+	test_expect_$expect "with hook (rebase ${mode:--i})" '
 		test_when_finished "\
 			git rebase --abort
-			git checkout -f master
+			git checkout -f main
 			git branch -D tmp" &&
 		git checkout -b tmp rebase-me &&
 		GIT_SEQUENCE_EDITOR="cp rebase-todo" &&
@@ -250,14 +253,14 @@
 test_have_prereq !REBASE_P || test_rebase success -p
 
 test_expect_success 'with hook (cherry-pick)' '
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other b &&
 	git cherry-pick rebase-1 &&
 	test "$(git log -1 --pretty=format:%s)" = "message (no editor)"
 '
 
 test_expect_success 'with hook and editor (cherry-pick)' '
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other b &&
 	git cherry-pick -e rebase-1 &&
 	test "$(git log -1 --pretty=format:%s)" = merge
@@ -270,7 +273,7 @@
 
 test_expect_success 'with failing hook' '
 
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	head=$(git rev-parse HEAD) &&
 	echo "more" >> file &&
 	git add file &&
@@ -280,7 +283,7 @@
 
 test_expect_success 'with failing hook (--no-verify)' '
 
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	head=$(git rev-parse HEAD) &&
 	echo "more" >> file &&
 	git add file &&
@@ -290,7 +293,7 @@
 
 test_expect_success 'with failing hook (merge)' '
 
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other HEAD@{1} &&
 	echo "more" >> file &&
 	git add file &&
@@ -304,8 +307,8 @@
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'with failing hook (cherry-pick)' '
-	test_when_finished "git checkout -f master" &&
+test_expect_success 'with failing hook (cherry-pick)' '
+	test_when_finished "git checkout -f main" &&
 	git checkout -B other b &&
 	test_must_fail git cherry-pick rebase-1 2>actual &&
 	test $(grep -c prepare-commit-msg actual) = 1
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 963fed6..2b72451 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -37,11 +37,11 @@
 	git checkout -b upstream &&
 	test_commit upstream1 &&
 	test_commit upstream2 &&
-	# leave the first commit on master as root because several
+	# leave the first commit on main as root because several
 	# tests depend on this case; for our upstream we only
 	# care about commit counts anyway, so a totally divergent
 	# history is OK
-	git checkout --orphan master
+	git checkout --orphan main
 '
 
 test_expect_success 'setup' '
@@ -88,7 +88,7 @@
 
 test_expect_success 'status --column' '
 	cat >expect <<\EOF &&
-# On branch master
+# On branch main
 # Your branch and '\''upstream'\'' have diverged,
 # and have 1 and 2 different commits each, respectively.
 #   (use "git pull" to merge the remote branch into yours)
@@ -109,17 +109,17 @@
 #
 EOF
 	COLUMNS=50 git -c status.displayCommentPrefix=true status --column="column dense" >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status --column status.displayCommentPrefix=false' '
 	strip_comments expect &&
 	COLUMNS=49 git -c status.displayCommentPrefix=false status --column="column dense" >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<\EOF
-# On branch master
+# On branch main
 # Your branch and 'upstream' have diverged,
 # and have 1 and 2 different commits each, respectively.
 #   (use "git pull" to merge the remote branch into yours)
@@ -144,19 +144,19 @@
 
 test_expect_success 'status with status.displayCommentPrefix=true' '
 	git -c status.displayCommentPrefix=true status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status with status.displayCommentPrefix=false' '
 	strip_comments expect &&
 	git -c status.displayCommentPrefix=false status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status -v' '
 	(cat expect && git diff --cached) >expect-with-v &&
 	git status -v >output &&
-	test_i18ncmp expect-with-v output
+	test_cmp expect-with-v output
 '
 
 test_expect_success 'status -v -v' '
@@ -167,7 +167,7 @@
 	 echo "Changes not staged for commit:" &&
 	 git -c diff.mnemonicprefix=true diff) >expect-with-v &&
 	git status -v -v >output &&
-	test_i18ncmp expect-with-v output
+	test_cmp expect-with-v output
 '
 
 test_expect_success 'setup fake editor' '
@@ -193,7 +193,7 @@
 '
 
 cat >expect <<\EOF
-On branch master
+On branch main
 Your branch and 'upstream' have diverged,
 and have 1 and 2 different commits each, respectively.
 
@@ -214,7 +214,7 @@
 test_expect_success 'status (advice.statusHints false)' '
 	test_config advice.statusHints false &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 
 '
 
@@ -266,7 +266,7 @@
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -296,7 +296,7 @@
 
 EOF
 	git status --ignored >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status with gitignore (nothing untracked)' '
@@ -331,7 +331,7 @@
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -358,7 +358,7 @@
 
 EOF
 	git status --ignored >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >.gitignore <<\EOF
@@ -368,7 +368,7 @@
 EOF
 
 cat >expect <<\EOF
-## master...upstream [ahead 1, behind 2]
+## main...upstream [ahead 1, behind 2]
  M dir1/modified
 A  dir2/added
 ?? dir1/untracked
@@ -380,7 +380,7 @@
 test_expect_success 'status -s -b' '
 
 	git status -s -b >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 
 '
 
@@ -390,7 +390,7 @@
 	git status -s -z -b >output &&
 	nul_to_q <output >output.q &&
 	mv output.q output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'setup dir3' '
@@ -401,7 +401,7 @@
 
 test_expect_success 'status -uno' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -418,18 +418,18 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status -uno >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles no)' '
 	test_config status.showuntrackedfiles no &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status -uno (advice.statusHints false)' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
 
@@ -443,7 +443,7 @@
 EOF
 	test_config advice.statusHints false &&
 	git status -uno >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect << EOF
@@ -463,7 +463,7 @@
 
 test_expect_success 'status -unormal' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -487,13 +487,13 @@
 
 EOF
 	git status -unormal >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles normal)' '
 	test_config status.showuntrackedfiles normal &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -518,7 +518,7 @@
 
 test_expect_success 'status -uall' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -543,13 +543,13 @@
 
 EOF
 	git status -uall >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles all)' '
 	test_config status.showuntrackedfiles all &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'teardown dir3' '
@@ -578,7 +578,7 @@
 
 test_expect_success 'status with relative paths' '
 	cat >expect <<\EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -601,7 +601,7 @@
 
 EOF
 	(cd dir1 && git status) >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<\EOF
@@ -646,7 +646,7 @@
 
 test_expect_success TTY 'status with color.ui' '
 	cat >expect <<\EOF &&
-On branch <GREEN>master<RESET>
+On branch <GREEN>main<RESET>
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -670,13 +670,13 @@
 EOF
 	test_config color.ui auto &&
 	test_terminal git status | test_decode_color >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success TTY 'status with color.status' '
 	test_config color.status auto &&
 	test_terminal git status | test_decode_color >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<\EOF
@@ -706,7 +706,7 @@
 '
 
 cat >expect <<\EOF
-## <YELLOW>master<RESET>...<CYAN>upstream<RESET> [ahead <YELLOW>1<RESET>, behind <CYAN>2<RESET>]
+## <YELLOW>main<RESET>...<CYAN>upstream<RESET> [ahead <YELLOW>1<RESET>, behind <CYAN>2<RESET>]
  <RED>M<RESET> dir1/modified
 <GREEN>A<RESET>  dir2/added
 <BLUE>??<RESET> dir1/untracked
@@ -718,7 +718,7 @@
 test_expect_success TTY 'status -s -b with color.status' '
 
 	test_terminal git status -s -b | test_decode_color >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 
 '
 
@@ -757,7 +757,7 @@
 
 	git status --porcelain -b >output &&
 	{
-		echo "## master...upstream [ahead 1, behind 2]" &&
+		echo "## main...upstream [ahead 1, behind 2]" &&
 		cat expect
 	} >tmp &&
 	mv tmp expect &&
@@ -769,7 +769,7 @@
 
 test_expect_success 'status without relative paths' '
 	cat >expect <<\EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -793,7 +793,7 @@
 EOF
 	test_config status.relativePaths false &&
 	(cd dir1 && git status) >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 
 '
 
@@ -843,7 +843,7 @@
 
 test_expect_success 'dry-run of partial commit excluding new file in index' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -860,7 +860,7 @@
 
 EOF
 	git commit --dry-run dir1/modified >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -897,7 +897,7 @@
 
 test_expect_success 'status submodule summary is disabled by default' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -921,13 +921,13 @@
 
 EOF
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 # we expect the same as the previous test
 test_expect_success 'status --untracked-files=all does not show submodule' '
 	git status --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -954,7 +954,7 @@
 
 test_expect_success 'status submodule summary' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 1 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -984,13 +984,13 @@
 EOF
 	git config status.submodulesummary 10 &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'status submodule summary with status.displayCommentPrefix=false' '
 	strip_comments expect &&
 	git -c status.displayCommentPrefix=false status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' '
@@ -1013,7 +1013,7 @@
 
 test_expect_success 'status submodule summary (clean submodule): commit' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1035,9 +1035,9 @@
 	git commit -m "commit submodule" &&
 	git config status.submodulesummary 10 &&
 	test_must_fail git commit --dry-run >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -1061,7 +1061,7 @@
 
 test_expect_success 'commit --dry-run submodule summary (--amend)' '
 	cat >expect <<EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1091,7 +1091,7 @@
 EOF
 	git config status.submodulesummary 10 &&
 	git commit --dry-run --amend >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
@@ -1113,7 +1113,7 @@
 
 test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
 	cat > expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1143,17 +1143,17 @@
 EOF
 	echo modified  sm/untracked &&
 	git status --ignore-submodules=untracked >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
 	test_config diff.ignoreSubmodules dirty &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --add -f .gitmodules submodule.subname.ignore untracked &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1163,14 +1163,14 @@
 	git config --add submodule.subname.ignore untracked &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config --remove-section -f .gitmodules submodule.subname
 '
 
 test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
 	git status --ignore-submodules=dirty >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
@@ -1180,7 +1180,7 @@
 	git config --add -f .gitmodules submodule.subname.ignore dirty &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1190,7 +1190,7 @@
 	git config --add submodule.subname.ignore dirty &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1198,14 +1198,14 @@
 test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
 	echo modified >sm/foo &&
 	git status --ignore-submodules=dirty >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
 	git config --add -f .gitmodules submodule.subname.ignore dirty &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1215,14 +1215,14 @@
 	git config --add submodule.subname.ignore dirty &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
 	cat > expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1253,14 +1253,14 @@
 
 EOF
 	git status --ignore-submodules=untracked > output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
 	git config --add -f .gitmodules submodule.subname.ignore untracked &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1270,7 +1270,7 @@
 	git config --add submodule.subname.ignore untracked &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1279,7 +1279,7 @@
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
 	cat > expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1314,14 +1314,14 @@
 
 EOF
 	git status --ignore-submodules=untracked > output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
 	git config --add -f .gitmodules submodule.subname.ignore untracked &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1331,20 +1331,20 @@
 	git config --add submodule.subname.ignore untracked &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
 	git status --ignore-submodules=dirty > output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
 	git config --add -f .gitmodules submodule.subname.ignore dirty &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1354,13 +1354,13 @@
 	git config --add submodule.subname.ignore dirty &&
 	git config --add submodule.subname.path sm &&
 	git status >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
 cat > expect << EOF
-; On branch master
+; On branch main
 ; Your branch and 'upstream' have diverged,
 ; and have 2 and 2 different commits each, respectively.
 ;   (use "git pull" to merge the remote branch into yours)
@@ -1398,7 +1398,7 @@
 test_expect_success "status (core.commentchar with submodule summary)" '
 	test_config core.commentchar ";" &&
 	git -c status.displayCommentPrefix=true status >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
@@ -1408,7 +1408,7 @@
 
 test_expect_success "--ignore-submodules=all suppresses submodule summary" '
 	cat > expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1429,12 +1429,12 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --ignore-submodules=all > output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '
 	cat > expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1460,7 +1460,7 @@
 	git config --add -f .gitmodules submodule.subname.ignore all &&
 	git config --add -f .gitmodules submodule.subname.path sm &&
 	git status > output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1470,7 +1470,7 @@
 	git config --add submodule.subname.ignore all &&
 	git config --add submodule.subname.path sm &&
 	git status > output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git config --remove-section submodule.subname &&
 	git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1554,7 +1554,7 @@
 	git reset HEAD^ &&
 	git add sm &&
 	cat >expect << EOF &&
-On branch master
+On branch main
 Your branch and '\''upstream'\'' have diverged,
 and have 2 and 2 different commits each, respectively.
   (use "git pull" to merge the remote branch into yours)
@@ -1571,7 +1571,7 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git commit -uno --dry-run >output &&
-	test_i18ncmp expect output &&
+	test_cmp expect output &&
 	git status -s --ignore-submodules=dirty >output &&
 	test_i18ngrep "^M. sm" output
 '
diff --git a/t/t7509-commit-authorship.sh b/t/t7509-commit-authorship.sh
index 500ab2f..ee6c474 100755
--- a/t/t7509-commit-authorship.sh
+++ b/t/t7509-commit-authorship.sh
@@ -18,11 +18,8 @@
 }
 
 test_expect_success '-C option copies authorship and message' '
-	echo "Initial" >foo &&
-	git add foo &&
-	test_tick &&
-	git commit -m "Initial Commit" --author Frigate\ \<flying@over.world\> &&
-	git tag Initial &&
+	test_commit --author Frigate\ \<flying@over.world\> \
+		"Initial Commit" foo Initial Initial &&
 	echo "Test 1" >>foo &&
 	test_tick &&
 	git commit -a -C Initial &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 6baaa1a..8df5a74 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='signed commit tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 GNUPGHOME_NOT_USED=$GNUPGHOME
 . "$TEST_DIRECTORY/lib-gpg.sh"
@@ -25,7 +28,7 @@
 	echo 3 >elif && git add elif &&
 	test_tick && git commit -m "third on side" &&
 
-	git checkout master &&
+	git checkout main &&
 	test_tick && git merge -S side &&
 	git tag merge &&
 
@@ -172,7 +175,7 @@
 	git cat-file commit initial >cat &&
 	grep -v -e "gpg: " -e "Warning: " show >show.commit &&
 	grep -e "gpg: " -e "Warning: " show >show.gpg &&
-	grep -v "^ " cat | grep -v "^$(test_oid header) " >cat.commit &&
+	grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
 	test_cmp show.commit commit &&
 	test_cmp show.gpg verify.2 &&
 	test_cmp cat.commit verify.1
@@ -334,4 +337,45 @@
 	test_cmp expect actual
 '
 
+
+test_expect_success GPG 'verify-commit verifies multiply signed commits' '
+	git init multiply-signed &&
+	cd multiply-signed &&
+	test_commit first &&
+	echo 1 >second &&
+	git add second &&
+	tree=$(git write-tree) &&
+	parent=$(git rev-parse HEAD^{commit}) &&
+	git commit --gpg-sign -m second &&
+	git cat-file commit HEAD &&
+	# Avoid trailing whitespace.
+	sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
+	Qtree $tree
+	Qparent $parent
+	Qauthor A U Thor <author@example.com> 1112912653 -0700
+	Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
+	Qgpgsig -----BEGIN PGP SIGNATURE-----
+	QZ
+	Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
+	Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
+	Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
+	Q =tQ0N
+	Q -----END PGP SIGNATURE-----
+	Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+	QZ
+	Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
+	Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
+	Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
+	Q =pIwP
+	Q -----END PGP SIGNATURE-----
+	Q
+	Qsecond
+	EOF
+	head=$(git hash-object -t commit -w commit) &&
+	git reset --hard $head &&
+	git verify-commit $head 2>actual &&
+	grep "Good signature from" actual &&
+	! grep "BAD signature from" actual
+'
+
 test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 29518e0..7f2956d 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -7,6 +7,9 @@
 
 test_description='git status advice'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
@@ -17,14 +20,14 @@
 	git config --global advice.statusuoption false &&
 	test_commit init main.txt init &&
 	git branch conflicts &&
-	test_commit on_master main.txt on_master &&
+	test_commit on_main main.txt on_main &&
 	git checkout conflicts &&
 	test_commit on_conflicts main.txt on_conflicts
 '
 
 
 test_expect_success 'status when conflicts unresolved' '
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	cat >expected <<\EOF &&
 On branch conflicts
 You have unmerged paths.
@@ -38,13 +41,13 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when conflicts resolved before commit' '
 	git reset --hard conflicts &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	echo one >main.txt &&
 	git add main.txt &&
 	cat >expected <<\EOF &&
@@ -58,12 +61,12 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'prepare for rebase conflicts' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b rebase_conflicts &&
 	test_commit one_rebase main.txt one &&
 	test_commit two_rebase main.txt two &&
@@ -90,7 +93,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -113,16 +116,16 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'prepare for rebase_i_conflicts' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b rebase_i_conflicts &&
 	test_commit one_unmerge main.txt one_unmerge &&
 	git branch rebase_i_conflicts_second &&
-	test_commit one_master main.txt one_master &&
+	test_commit one_main main.txt one_main &&
 	git checkout rebase_i_conflicts_second &&
 	test_commit one_second main.txt one_second
 '
@@ -151,7 +154,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -177,12 +180,12 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when rebasing -i in edit mode' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b rebase_i_edit &&
 	test_commit one_rebase_i main.txt one &&
 	test_commit two_rebase_i main.txt two &&
@@ -207,12 +210,12 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when splitting a commit' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b split_commit &&
 	test_commit one_split main.txt one &&
 	test_commit two_split main.txt two &&
@@ -246,12 +249,12 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status after editing the last commit with --amend during a rebase -i' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b amend_last &&
 	test_commit one_amend main.txt one &&
 	test_commit two_amend main.txt two &&
@@ -279,12 +282,12 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'prepare for several edits' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b several_edits &&
 	test_commit one_edits main.txt one &&
 	test_commit two_edits main.txt two &&
@@ -318,7 +321,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -353,7 +356,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -384,7 +387,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -415,7 +418,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -451,7 +454,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -483,7 +486,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -516,7 +519,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -554,7 +557,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -588,12 +591,12 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'prepare am_session' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b am_session &&
 	test_commit one_am one.txt "one" &&
 	test_commit two_am two.txt "two" &&
@@ -616,7 +619,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -638,7 +641,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -661,12 +664,12 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when bisecting' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b bisect &&
 	test_commit one_bisect main.txt one &&
 	test_commit two_bisect main.txt two &&
@@ -684,12 +687,12 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b statushints_disabled &&
 	test_when_finished "git config --local advice.statushints true" &&
 	git config --local advice.statushints false &&
@@ -709,12 +712,12 @@
 no changes added to commit
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
 test_expect_success 'prepare for cherry-pick conflicts' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b cherry_branch &&
 	test_commit one_cherry main.txt one &&
 	test_commit two_cherries main.txt two &&
@@ -743,7 +746,7 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 
@@ -767,7 +770,7 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status when cherry-picking after committing conflict resolution' '
@@ -786,7 +789,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status shows cherry-pick with invalid oid' '
@@ -795,7 +798,7 @@
 	git status --untracked-files=no >actual 2>err &&
 	git cherry-pick --quit &&
 	test_must_be_empty err &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status does not show error if .git/sequencer is a file' '
@@ -813,7 +816,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual &&
+	test_cmp expected actual &&
 
 	git reset --hard HEAD^ &&
 	cat >expected <<\EOF &&
@@ -821,11 +824,11 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status while reverting commit (conflicts)' '
-	git checkout master &&
+	git checkout main &&
 	echo before >to-revert.txt &&
 	test_commit before to-revert.txt &&
 	echo old >to-revert.txt &&
@@ -835,7 +838,7 @@
 	TO_REVERT=$(git rev-parse --short HEAD^) &&
 	test_must_fail git revert $TO_REVERT &&
 	cat >expected <<EOF &&
-On branch master
+On branch main
 You are currently reverting commit $TO_REVERT.
   (fix conflicts and run "git revert --continue")
   (use "git revert --skip" to skip this patch)
@@ -849,14 +852,14 @@
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status while reverting commit (conflicts resolved)' '
 	echo reverted >to-revert.txt &&
 	git add to-revert.txt &&
 	cat >expected <<EOF &&
-On branch master
+On branch main
 You are currently reverting commit $TO_REVERT.
   (all conflicts fixed: run "git revert --continue")
   (use "git revert --skip" to skip this patch)
@@ -869,17 +872,17 @@
 Untracked files not listed (use -u option to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status after reverting commit' '
 	git revert --continue &&
 	cat >expected <<\EOF &&
-On branch master
+On branch main
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status while reverting after committing conflict resolution' '
@@ -889,7 +892,7 @@
 	echo reverted >to-revert.txt &&
 	git commit -a &&
 	cat >expected <<EOF &&
-On branch master
+On branch main
 Revert currently in progress.
   (run "git revert --continue" to continue)
   (use "git revert --skip" to skip this patch)
@@ -898,11 +901,11 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'prepare for different number of commits rebased' '
-	git reset --hard master &&
+	git reset --hard main &&
 	git checkout -b several_commits &&
 	test_commit one_commit main.txt one &&
 	test_commit two_commit main.txt two &&
@@ -928,7 +931,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status: two commands done with some white lines in done file' '
@@ -956,7 +959,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status: two remaining commands with some white lines in todo file' '
@@ -985,7 +988,7 @@
 nothing to commit (use -u to show untracked files)
 EOF
 	git status --untracked-files=no >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_expect_success 'status: handle not-yet-started rebase -i gracefully' '
@@ -1004,7 +1007,7 @@
 
 nothing to commit (use -u to show untracked files)
 EOF
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_done
diff --git a/t/t7517-per-repo-email.sh b/t/t7517-per-repo-email.sh
index b2401ce..405420a 100755
--- a/t/t7517-per-repo-email.sh
+++ b/t/t7517-per-repo-email.sh
@@ -6,6 +6,9 @@
 
 test_description='per-repo forced setting of email address'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup a likely user.useConfigOnly use case' '
@@ -47,24 +50,24 @@
 
 test_expect_success 'fast-forward rebase does not care about ident' '
 	git checkout -B tmp side-without-commit &&
-	git rebase master
+	git rebase main
 '
 
 test_expect_success 'non-fast-forward rebase refuses to write commits' '
 	test_when_finished "git rebase --abort || true" &&
 	git checkout -B tmp side-with-commit &&
-	test_must_fail git rebase master
+	test_must_fail git rebase main
 '
 
 test_expect_success 'fast-forward rebase does not care about ident (interactive)' '
 	git checkout -B tmp side-without-commit &&
-	git rebase -i master
+	git rebase -i main
 '
 
 test_expect_success 'non-fast-forward rebase refuses to write commits (interactive)' '
 	test_when_finished "git rebase --abort || true" &&
 	git checkout -B tmp side-with-commit &&
-	test_must_fail git rebase -i master
+	test_must_fail git rebase -i main
 '
 
 test_expect_success 'noop interactive rebase does not care about ident' '
@@ -75,14 +78,14 @@
 test_expect_success REBASE_P \
 	'fast-forward rebase does not care about ident (preserve)' '
 	git checkout -B tmp side-without-commit &&
-	git rebase -p master
+	git rebase -p main
 '
 
 test_expect_success REBASE_P \
 	'non-fast-forward rebase refuses to write commits (preserve)' '
 	test_when_finished "git rebase --abort || true" &&
 	git checkout -B tmp side-with-commit &&
-	test_must_fail git rebase -p master
+	test_must_fail git rebase -p main
 '
 
 test_expect_success 'author.name overrides user.name' '
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index fbfdcca..45d025f 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -236,7 +236,7 @@
 	git reset HEAD~1 &&
 	git status >actual &&
 	git -c core.fsmonitor= status >expect &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 # test fsmonitor with and without preloadIndex
@@ -273,7 +273,7 @@
 			git add dir2/new &&
 			git status >actual &&
 			git -c core.fsmonitor= status >expect &&
-			test_i18ncmp expect actual
+			test_cmp expect actual
 		'
 
 		# Make sure it's actually skipping the check for modified and untracked
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
index 9179094..a88b02b 100755
--- a/t/t7521-ignored-mode.sh
+++ b/t/t7521-ignored-mode.sh
@@ -30,7 +30,7 @@
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on directory with tracked & ignored files' '
@@ -55,7 +55,7 @@
 	git commit -m "commit tracked files" &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on directory with untracked and ignored files' '
@@ -80,7 +80,7 @@
 		dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify status matching ignored files on ignored directory' '
@@ -96,7 +96,7 @@
 		ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
@@ -117,7 +117,7 @@
 	git add -f ignored_dir/tracked &&
 	git commit -m "Force add file in ignored directory" &&
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
@@ -136,7 +136,7 @@
 		ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
 		untracked_dir/untracked &&
 	git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
@@ -155,7 +155,7 @@
 		ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
 		untracked_dir/untracked &&
 	git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
@@ -176,7 +176,7 @@
 	git add -f ignored_dir/tracked &&
 	git commit -m "Force add file in ignored directory" &&
 	git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=no' '
@@ -191,7 +191,7 @@
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=no --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '
@@ -210,7 +210,7 @@
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '
@@ -227,7 +227,7 @@
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&
-	test_i18ncmp expect output
+	test_cmp expect output
 '
 
 test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 1c85f75..1cbc971 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -14,9 +14,9 @@
     ! [c4] c4
      ! [c5] c5
       ! [c6] c6
-       * [master] Merge commit 'c1'
+       * [main] Merge commit 'c1'
 --------
-       - [master] Merge commit 'c1'
+       - [main] Merge commit 'c1'
  +     * [c1] commit 1
       +  [c6] c6
      +   [c5] c5
@@ -26,6 +26,9 @@
 +++++++* [c0] commit 0
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
@@ -203,13 +206,13 @@
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge from unborn branch' '
-	git checkout -f master &&
+	git checkout -f main &&
 	test_might_fail git branch -D kid &&
 
 	echo "OBJID HEAD@{0}: initial pull" >reflog.expected &&
 
 	git checkout --orphan kid &&
-	test_when_finished "git checkout -f master" &&
+	test_when_finished "git checkout -f main" &&
 	git rm -fr . &&
 	test_tick &&
 	git merge --ff-only c1 &&
@@ -269,7 +272,7 @@
 	EOF
 	git cat-file commit HEAD >raw &&
 	sed -e "1,/^$/d" raw >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
@@ -293,7 +296,7 @@
 	EOF
 	git cat-file commit HEAD >raw &&
 	sed -e "1,/^$/d" raw >actual &&
-	test_i18ncmp expect actual
+	test_cmp expect actual
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
@@ -413,7 +416,7 @@
 
 test_expect_success 'merge c1 with c2 (no-commit in config)' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "--no-commit" &&
+	test_config branch.main.mergeoptions "--no-commit" &&
 	git merge c2 &&
 	verify_merge file result.1-5 &&
 	verify_head $c1 &&
@@ -427,7 +430,7 @@
 	git merge --log c2 &&
 	git show -s --pretty=tformat:%s%n%b >expect &&
 
-	test_config branch.master.mergeoptions "--log" &&
+	test_config branch.main.mergeoptions "--log" &&
 	git reset --hard c1 &&
 	git merge c2 &&
 	git show -s --pretty=tformat:%s%n%b >actual &&
@@ -440,7 +443,7 @@
 	git merge c2 &&
 	git show -s --pretty=tformat:%s%n%b >expect &&
 
-	test_config branch.master.mergeoptions "--no-log" &&
+	test_config branch.main.mergeoptions "--no-log" &&
 	test_config merge.log "true" &&
 	git reset --hard c1 &&
 	git merge c2 &&
@@ -451,7 +454,7 @@
 
 test_expect_success 'merge c1 with c2 (squash in config)' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "--squash" &&
+	test_config branch.main.mergeoptions "--squash" &&
 	git merge c2 &&
 	verify_merge file result.1-5 &&
 	verify_head $c1 &&
@@ -463,7 +466,7 @@
 
 test_expect_success 'override config option -n with --summary' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "-n" &&
+	test_config branch.main.mergeoptions "-n" &&
 	test_tick &&
 	git merge --summary c2 >diffstat.txt &&
 	verify_merge file result.1-5 msg.1-5 &&
@@ -477,7 +480,7 @@
 
 test_expect_success 'override config option -n with --stat' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "-n" &&
+	test_config branch.main.mergeoptions "-n" &&
 	test_tick &&
 	git merge --stat c2 >diffstat.txt &&
 	verify_merge file result.1-5 msg.1-5 &&
@@ -493,7 +496,7 @@
 
 test_expect_success 'override config option --stat' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "--stat" &&
+	test_config branch.main.mergeoptions "--stat" &&
 	test_tick &&
 	git merge -n c2 >diffstat.txt &&
 	verify_merge file result.1-5 msg.1-5 &&
@@ -509,7 +512,7 @@
 
 test_expect_success 'merge c1 with c2 (override --no-commit)' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "--no-commit" &&
+	test_config branch.main.mergeoptions "--no-commit" &&
 	test_tick &&
 	git merge --commit c2 &&
 	verify_merge file result.1-5 msg.1-5 &&
@@ -520,7 +523,7 @@
 
 test_expect_success 'merge c1 with c2 (override --squash)' '
 	git reset --hard c1 &&
-	test_config branch.master.mergeoptions "--squash" &&
+	test_config branch.main.mergeoptions "--squash" &&
 	test_tick &&
 	git merge --no-squash c2 &&
 	verify_merge file result.1-5 msg.1-5 &&
@@ -549,9 +552,9 @@
 '
 test_debug 'git log --graph --decorate --oneline --all'
 
-test_expect_success 'combine branch.master.mergeoptions with merge.ff' '
+test_expect_success 'combine branch.main.mergeoptions with merge.ff' '
 	git reset --hard c0 &&
-	test_config branch.master.mergeoptions "--ff" &&
+	test_config branch.main.mergeoptions "--ff" &&
 	test_config merge.ff "false" &&
 	test_tick &&
 	git merge c1 &&
@@ -593,7 +596,7 @@
 
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
 	git reset --hard c0 &&
-	test_config branch.master.mergeoptions "--no-ff" &&
+	test_config branch.main.mergeoptions "--no-ff" &&
 	git merge --ff c1 &&
 	verify_merge file result.1 &&
 	verify_head $c1
@@ -606,7 +609,7 @@
 	test_must_be_empty msg.act &&
 
 	git reset --hard c0 &&
-	test_config branch.master.mergeoptions "--no-ff" &&
+	test_config branch.main.mergeoptions "--no-ff" &&
 	git merge --no-log c2 &&
 	git show -s --pretty=format:%b HEAD >msg.act &&
 	test_must_be_empty msg.act &&
@@ -950,10 +953,10 @@
 	git add file &&
 	git commit -m base &&
 
-	# one side changes the first line of each to "master"
-	sed s/-1/-master/ file >tmp &&
+	# one side changes the first line of each to "main"
+	sed s/-1/-main/ file >tmp &&
 	mv tmp file &&
-	git commit -am master &&
+	git commit -am main &&
 
 	# and the other to "side"; merging the two will
 	# yield 256 separate conflicts
@@ -965,12 +968,12 @@
 
 test_expect_success 'merge detects mod-256 conflicts (recursive)' '
 	git reset --hard &&
-	test_must_fail git merge -s recursive master
+	test_must_fail git merge -s recursive main
 '
 
 test_expect_success 'merge detects mod-256 conflicts (resolve)' '
 	git reset --hard &&
-	test_must_fail git merge -s resolve master
+	test_must_fail git merge -s resolve main
 '
 
 test_expect_success 'merge nothing into void' '
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 6774e9d..52e8ccc 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -29,11 +29,8 @@
 
 test_expect_success 'pull.rebase not set' '
 	git reset --hard c0 &&
-	git -c color.advice=always pull . c1 2>err &&
-	test_decode_color <err >decoded &&
-	test_i18ngrep "<YELLOW>hint: " decoded &&
-	test_i18ngrep "Pulling without specifying how to reconcile" decoded
-
+	git pull . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
 '
 
 test_expect_success 'pull.rebase not set and pull.ff=true' '
@@ -87,6 +84,65 @@
 	test_i18ngrep ! "Pulling without specifying how to reconcile" err
 '
 
+test_expect_success 'pull.rebase not set (not-fast-forward)' '
+	git reset --hard c2 &&
+	git -c color.advice=always pull . c1 2>err &&
+	test_decode_color <err >decoded &&
+	test_i18ngrep "<YELLOW>hint: " decoded &&
+	test_i18ngrep "Pulling without specifying how to reconcile" decoded
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
+	git reset --hard c2 &&
+	test_config pull.ff true &&
+	git pull . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
+	git reset --hard c2 &&
+	test_config pull.ff false &&
+	git pull . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
+	git reset --hard c2 &&
+	test_config pull.ff only &&
+	test_must_fail git pull . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
+	git reset --hard c2 &&
+	git pull --rebase . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
+	git reset --hard c2 &&
+	git pull --no-rebase . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
+	git reset --hard c2 &&
+	git pull --ff . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
+	git reset --hard c2 &&
+	git pull --no-ff . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
+test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
+	git reset --hard c2 &&
+	test_must_fail git pull --ff-only . c1 2>err &&
+	test_i18ngrep ! "Pulling without specifying how to reconcile" err
+'
+
 test_expect_success 'merge c1 with c2' '
 	git reset --hard c1 &&
 	test -f c0.c &&
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index 13859ec..a9c816b 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -66,7 +66,7 @@
 test_expect_success 'merge output uses pretty names' '
 	git reset --hard c1 &&
 	git merge c2 c3 c4 >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 cat >expected <<\EOF
@@ -84,7 +84,7 @@
 		rm expected.tmp
 	fi &&
 	GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 cat >expected <<\EOF
@@ -101,7 +101,7 @@
 test_expect_success 'merge fast-forward output uses pretty names' '
 	git reset --hard c0 &&
 	git merge c1 c2 >actual &&
-	test_i18ncmp expected actual
+	test_cmp expected actual
 '
 
 test_done
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
index 8e8c4d7..81fb7c4 100755
--- a/t/t7606-merge-custom.sh
+++ b/t/t7606-merge-custom.sh
@@ -4,7 +4,7 @@
 
 Testing a custom strategy.
 
-*   (HEAD, master) Merge commit 'c3'
+*   (HEAD, main) Merge commit 'c3'
 |\
 | * (tag: c3) c3
 * | (tag: c1) c1
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
index 8e7e0a5..0b908ab 100755
--- a/t/t7608-merge-messages.sh
+++ b/t/t7608-merge-messages.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='test auto-generated merge messages'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 check_oneline() {
@@ -10,51 +13,51 @@
 }
 
 test_expect_success 'merge local branch' '
-	test_commit master-1 &&
+	test_commit main-1 &&
 	git checkout -b local-branch &&
 	test_commit branch-1 &&
-	git checkout master &&
-	test_commit master-2 &&
+	git checkout main &&
+	test_commit main-2 &&
 	git merge local-branch &&
 	check_oneline "Merge branch Qlocal-branchQ"
 '
 
 test_expect_success 'merge octopus branches' '
-	git checkout -b octopus-a master &&
+	git checkout -b octopus-a main &&
 	test_commit octopus-1 &&
-	git checkout -b octopus-b master &&
+	git checkout -b octopus-b main &&
 	test_commit octopus-2 &&
-	git checkout master &&
+	git checkout main &&
 	git merge octopus-a octopus-b &&
 	check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
 '
 
 test_expect_success 'merge tag' '
-	git checkout -b tag-branch master &&
+	git checkout -b tag-branch main &&
 	test_commit tag-1 &&
-	git checkout master &&
-	test_commit master-3 &&
+	git checkout main &&
+	test_commit main-3 &&
 	git merge tag-1 &&
 	check_oneline "Merge tag Qtag-1Q"
 '
 
 test_expect_success 'ambiguous tag' '
-	git checkout -b ambiguous master &&
+	git checkout -b ambiguous main &&
 	test_commit ambiguous &&
-	git checkout master &&
-	test_commit master-4 &&
+	git checkout main &&
+	test_commit main-4 &&
 	git merge ambiguous &&
 	check_oneline "Merge tag QambiguousQ"
 '
 
 test_expect_success 'remote-tracking branch' '
-	git checkout -b remote master &&
+	git checkout -b remote main &&
 	test_commit remote-1 &&
-	git update-ref refs/remotes/origin/master remote &&
-	git checkout master &&
-	test_commit master-5 &&
-	git merge origin/master &&
-	check_oneline "Merge remote-tracking branch Qorigin/masterQ"
+	git update-ref refs/remotes/origin/main remote &&
+	git checkout main &&
+	test_commit main-5 &&
+	git merge origin/main &&
+	check_oneline "Merge remote-tracking branch Qorigin/mainQ"
 '
 
 test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 6ac75b5..8cc6472 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -7,22 +7,25 @@
 
 Testing basic merge tool invocation'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # All the mergetool test work by checking out a temporary branch based
-# off 'branch1' and then merging in master and checking the results of
+# off 'branch1' and then merging in main and checking the results of
 # running mergetool
 
 test_expect_success 'setup' '
 	test_config rerere.enabled true &&
-	echo master >file1 &&
-	echo master spaced >"spaced name" &&
-	echo master file11 >file11 &&
-	echo master file12 >file12 &&
-	echo master file13 >file13 &&
-	echo master file14 >file14 &&
+	echo main >file1 &&
+	echo main spaced >"spaced name" &&
+	echo main file11 >file11 &&
+	echo main file12 >file12 &&
+	echo main file13 >file13 &&
+	echo main file14 >file14 &&
 	mkdir subdir &&
-	echo master sub >subdir/file3 &&
+	echo main sub >subdir/file3 &&
 	test_create_repo submod &&
 	(
 		cd submod &&
@@ -34,7 +37,7 @@
 	git add file1 "spaced name" file1[1-4] subdir/file3 .gitmodules submod &&
 	git commit -m "add initial versions" &&
 
-	git checkout -b branch1 master &&
+	git checkout -b branch1 main &&
 	git submodule update -N &&
 	echo branch1 change >file1 &&
 	echo branch1 newfile >file2 &&
@@ -71,39 +74,39 @@
 	test_write_lines one two 3 >c/c/file.txt &&
 	git commit -a -m"move to c" &&
 
-	git checkout -b stash1 master &&
+	git checkout -b stash1 main &&
 	echo stash1 change file11 >file11 &&
 	git add file11 &&
 	git commit -m "stash1 changes" &&
 
-	git checkout -b stash2 master &&
+	git checkout -b stash2 main &&
 	echo stash2 change file11 >file11 &&
 	git add file11 &&
 	git commit -m "stash2 changes" &&
 
-	git checkout master &&
+	git checkout main &&
 	git submodule update -N &&
-	echo master updated >file1 &&
-	echo master new >file2 &&
-	echo master updated spaced >"spaced name" &&
-	echo master both added >both &&
-	echo master updated file12 >file12 &&
-	echo master updated file14 >file14 &&
-	echo master new sub >subdir/file3 &&
+	echo main updated >file1 &&
+	echo main new >file2 &&
+	echo main updated spaced >"spaced name" &&
+	echo main both added >both &&
+	echo main updated file12 >file12 &&
+	echo main updated file14 >file14 &&
+	echo main new sub >subdir/file3 &&
 	(
 		cd submod &&
-		echo master submodule >bar &&
+		echo main submodule >bar &&
 		git add bar &&
-		git commit -m "Add bar on master" &&
-		git checkout -b submod-master
+		git commit -m "Add bar on main" &&
+		git checkout -b submod-main
 	) &&
 	git add file1 "spaced name" file12 file14 file2 subdir/file3 submod &&
 	git add both &&
 	git rm file11 &&
-	git commit -m "master updates" &&
+	git commit -m "main updates" &&
 
 	git clean -fdx &&
-	git checkout -b order-file-start master &&
+	git checkout -b order-file-start main &&
 	echo start >a &&
 	echo start >b &&
 	git add a b &&
@@ -130,7 +133,7 @@
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	yes "" | git mergetool both &&
 	yes "" | git mergetool file1 file1 &&
 	yes "" | git mergetool file2 "spaced name" &&
@@ -138,11 +141,11 @@
 	yes "d" | git mergetool file11 &&
 	yes "d" | git mergetool file12 &&
 	yes "l" | git mergetool submod &&
-	echo "master updated" >expect &&
+	echo "main updated" >expect &&
 	test_cmp expect file1 &&
-	echo "master new" >expect &&
+	echo "main new" >expect &&
 	test_cmp expect file2 &&
-	echo "master new sub" >expect &&
+	echo "main new sub" >expect &&
 	test_cmp expect subdir/file3 &&
 	echo "branch1 submodule" >expect &&
 	test_cmp expect submod/bar &&
@@ -156,7 +159,7 @@
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	yes "" | git mergetool --gui both &&
 	yes "" | git mergetool -g file1 file1 &&
 	yes "" | git mergetool --gui file2 "spaced name" &&
@@ -164,11 +167,11 @@
 	yes "d" | git mergetool --gui file11 &&
 	yes "d" | git mergetool --gui file12 &&
 	yes "l" | git mergetool --gui submod &&
-	echo "gui master updated" >expect &&
+	echo "gui main updated" >expect &&
 	test_cmp expect file1 &&
-	echo "gui master new" >expect &&
+	echo "gui main new" >expect &&
 	test_cmp expect file2 &&
-	echo "gui master new sub" >expect &&
+	echo "gui main new sub" >expect &&
 	test_cmp expect subdir/file3 &&
 	echo "branch1 submodule" >expect &&
 	test_cmp expect submod/bar &&
@@ -179,7 +182,7 @@
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	yes "" | git mergetool --gui both &&
 	yes "" | git mergetool -g file1 file1 &&
 	yes "" | git mergetool --gui file2 "spaced name" &&
@@ -187,11 +190,11 @@
 	yes "d" | git mergetool --gui file11 &&
 	yes "d" | git mergetool --gui file12 &&
 	yes "l" | git mergetool --gui submod &&
-	echo "master updated" >expect &&
+	echo "main updated" >expect &&
 	test_cmp expect file1 &&
-	echo "master new" >expect &&
+	echo "main new" >expect &&
 	test_cmp expect file2 &&
-	echo "master new sub" >expect &&
+	echo "main new sub" >expect &&
 	test_cmp expect subdir/file3 &&
 	echo "branch1 submodule" >expect &&
 	test_cmp expect submod/bar &&
@@ -206,7 +209,7 @@
 	# test_when_finished is LIFO.)
 	test_config core.autocrlf true &&
 	git checkout -b test$test_count branch1 &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	yes "" | git mergetool file1 &&
 	yes "" | git mergetool file2 &&
 	yes "" | git mergetool "spaced name" &&
@@ -215,11 +218,11 @@
 	yes "d" | git mergetool file11 &&
 	yes "d" | git mergetool file12 &&
 	yes "r" | git mergetool submod &&
-	test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
-	test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
-	test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+	test "$(printf x | cat file1 -)" = "$(printf "main updated\r\nx")" &&
+	test "$(printf x | cat file2 -)" = "$(printf "main new\r\nx")" &&
+	test "$(printf x | cat subdir/file3 -)" = "$(printf "main new sub\r\nx")" &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git commit -m "branch1 resolved with mergetool - autocrlf"
 '
@@ -230,9 +233,9 @@
 	git submodule update -N &&
 	(
 		cd subdir &&
-		test_must_fail git merge master &&
+		test_must_fail git merge main &&
 		yes "" | git mergetool file3 &&
-		echo "master new sub" >expect &&
+		echo "main new sub" >expect &&
 		test_cmp expect file3
 	)
 '
@@ -243,7 +246,7 @@
 	git submodule update -N &&
 	(
 		cd subdir &&
-		test_must_fail git merge master &&
+		test_must_fail git merge main &&
 		yes "" | git mergetool file3 &&
 		yes "" | git mergetool ../file1 &&
 		yes "" | git mergetool ../file2 ../spaced\ name &&
@@ -251,9 +254,9 @@
 		yes "d" | git mergetool ../file11 &&
 		yes "d" | git mergetool ../file12 &&
 		yes "l" | git mergetool ../submod &&
-		echo "master updated" >expect &&
+		echo "main updated" >expect &&
 		test_cmp expect ../file1 &&
-		echo "master new" >expect &&
+		echo "main new" >expect &&
 		test_cmp expect ../file2 &&
 		echo "branch1 submodule" >expect &&
 		test_cmp expect ../submod/bar &&
@@ -265,7 +268,7 @@
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "d" | git mergetool file11 &&
 	yes "d" | git mergetool file12 &&
@@ -280,17 +283,17 @@
 	test_config rerere.enabled false &&
 	(
 		cd subdir &&
-		test_must_fail git merge master &&
+		test_must_fail git merge main &&
 		yes "r" | git mergetool ../submod &&
 		yes "d" "d" | git mergetool --no-prompt &&
-		echo "master updated" >expect &&
+		echo "main updated" >expect &&
 		test_cmp expect ../file1 &&
-		echo "master new" >expect &&
+		echo "main new" >expect &&
 		test_cmp expect ../file2 &&
-		echo "master new sub" >expect &&
+		echo "main new sub" >expect &&
 		test_cmp expect file3 &&
 		( cd .. && git submodule update -N ) &&
-		echo "master submodule" >expect &&
+		echo "main submodule" >expect &&
 		test_cmp expect ../submod/bar &&
 		git commit -m "branch2 resolved by mergetool from subdir"
 	)
@@ -303,17 +306,17 @@
 	rm -rf .git/rr-cache &&
 	(
 		cd subdir &&
-		test_must_fail git merge master &&
+		test_must_fail git merge main &&
 		yes "r" | git mergetool ../submod &&
 		yes "d" "d" | git mergetool --no-prompt &&
-		echo "master updated" >expect &&
+		echo "main updated" >expect &&
 		test_cmp expect ../file1 &&
-		echo "master new" >expect &&
+		echo "main new" >expect &&
 		test_cmp expect ../file2 &&
-		echo "master new sub" >expect &&
+		echo "main new sub" >expect &&
 		test_cmp expect file3 &&
 		( cd .. && git submodule update -N ) &&
-		echo "master submodule" >expect &&
+		echo "main submodule" >expect &&
 		test_cmp expect ../submod/bar &&
 		git commit -m "branch2 resolved by mergetool from subdir"
 	)
@@ -325,7 +328,7 @@
 	rm -rf .git/rr-cache &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	yes "l" | git mergetool --no-prompt submod &&
 	yes "d" "d" | git mergetool --no-prompt &&
 	git submodule update -N &&
@@ -366,11 +369,11 @@
 	test_config rerere.enabled false &&
 	git checkout -b test$test_count branch1 &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 
 	yes "" | git mergetool subdir &&
 
-	echo "master new sub" >expect &&
+	echo "main new sub" >expect &&
 	test_cmp expect subdir/file3
 '
 
@@ -434,7 +437,7 @@
 	git rm --cached submod &&
 	git commit -m "Submodule deleted from branch" &&
 	git checkout -b test$test_count.a test$test_count &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
 	yes "" | git mergetool both &&
@@ -444,7 +447,7 @@
 	echo "branch1 submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
@@ -453,7 +456,7 @@
 	mv submod submod-movedaside &&
 	git checkout -b test$test_count.b test$test_count &&
 	git submodule update -N &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
 	yes "" | git mergetool both &&
@@ -465,7 +468,7 @@
 	git commit -m "Merge resolved by deleting module" &&
 
 	mv submod-movedaside submod &&
-	git checkout -b test$test_count.c master &&
+	git checkout -b test$test_count.c main &&
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
@@ -481,7 +484,7 @@
 	git commit -m "Merge resolved by deleting module" &&
 	mv submod.orig submod &&
 
-	git checkout -b test$test_count.d master &&
+	git checkout -b test$test_count.d main &&
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
@@ -489,10 +492,10 @@
 	yes "" | git mergetool both &&
 	yes "d" | git mergetool file11 file12 &&
 	yes "l" | git mergetool submod &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
@@ -509,7 +512,7 @@
 	git add submod &&
 	git commit -m "Submodule path becomes file" &&
 	git checkout -b test$test_count.a branch1 &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
 	yes "" | git mergetool both &&
@@ -519,7 +522,7 @@
 	echo "branch1 submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
@@ -527,7 +530,7 @@
 
 	mv submod submod-movedaside &&
 	git checkout -b test$test_count.b test$test_count &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
 	yes "" | git mergetool both &&
@@ -547,7 +550,7 @@
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping file" &&
 
-	git checkout -b test$test_count.c master &&
+	git checkout -b test$test_count.c main &&
 	rmdir submod && mv submod-movedaside submod &&
 	test ! -e submod.orig &&
 	git submodule update -N &&
@@ -573,7 +576,7 @@
 	test "$output" = "No files need merging" &&
 	git commit -m "Merge resolved by keeping file" &&
 
-	git checkout -b test$test_count.d master &&
+	git checkout -b test$test_count.d main &&
 	rmdir submod && mv submod.orig submod &&
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
@@ -586,10 +589,10 @@
 	then
 		yes "d" | git mergetool submod~test19
 	fi &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	output="$(git mergetool --no-prompt)" &&
 	test "$output" = "No files need merging" &&
@@ -672,7 +675,7 @@
 	git add submod/file16 &&
 	git commit -m "Submodule path becomes directory" &&
 
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	yes "l" | git mergetool submod &&
 	echo "not a submodule" >expect &&
@@ -680,14 +683,14 @@
 	rm -rf submod.orig &&
 
 	git reset --hard &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	test -n "$(git ls-files -u)" &&
 	test ! -e submod.orig &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		yes "r" | git mergetool submod~master &&
+		yes "r" | git mergetool submod~main &&
 		git mv submod submod.orig &&
-		git mv submod~master submod
+		git mv submod~main submod
 	else
 		yes "r" | git mergetool submod
 	fi &&
@@ -698,18 +701,18 @@
 	mv submod-movedaside/.git submod &&
 	( cd submod && git clean -f && git reset --hard ) &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 	git reset --hard &&
 	rm -rf submod-movedaside &&
 
-	git checkout -b test$test_count.c master &&
+	git checkout -b test$test_count.c main &&
 	git submodule update -N &&
 	test_must_fail git merge test$test_count &&
 	test -n "$(git ls-files -u)" &&
 	yes "l" | git mergetool submod &&
 	git submodule update -N &&
-	echo "master submodule" >expect &&
+	echo "main submodule" >expect &&
 	test_cmp expect submod/bar &&
 
 	git reset --hard &&
@@ -721,7 +724,7 @@
 	echo "not a submodule" >expect &&
 	test_cmp expect submod/file16 &&
 
-	git reset --hard master &&
+	git reset --hard main &&
 	( cd submod && git clean -f && git reset --hard ) &&
 	git submodule update -N
 '
@@ -729,7 +732,7 @@
 test_expect_success 'file with no base' '
 	test_when_finished "git reset --hard" &&
 	git checkout -b test$test_count branch1 &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git mergetool --no-prompt --tool mybase -- both &&
 	test_must_be_empty both
 '
@@ -739,9 +742,9 @@
 	git checkout -b test$test_count branch1 &&
 	test_config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
 	test_config mergetool.defaults.trustExitCode true &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git mergetool --no-prompt --tool defaults -- both &&
-	echo master both added >expected &&
+	echo main both added >expected &&
 	test_cmp expected both
 '
 
@@ -751,7 +754,7 @@
 	test_config mergetool.writeToTemp false &&
 	test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
 	test_config mergetool.myecho.trustExitCode true &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git mergetool --no-prompt --tool myecho -- both >actual &&
 	grep ^\./both_LOCAL_ actual
 '
@@ -768,7 +771,7 @@
 	test_config mergetool.writeToTemp true &&
 	test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" &&
 	test_config mergetool.myecho.trustExitCode true &&
-	test_must_fail git merge master &&
+	test_must_fail git merge main &&
 	git mergetool --no-prompt --tool myecho -- both >actual &&
 	! grep ^\./both_LOCAL_ actual &&
 	grep /both_LOCAL_ actual
@@ -839,4 +842,22 @@
 	grep meld mergetools
 '
 
+test_expect_success 'mergetool hideResolved' '
+	test_config mergetool.hideResolved true &&
+	test_when_finished "git reset --hard" &&
+	git checkout -b test${test_count}_b main &&
+	test_write_lines >file1 base "" a &&
+	git commit -a -m "base" &&
+	test_write_lines >file1 base "" c &&
+	git commit -a -m "remote update" &&
+	git checkout -b test${test_count}_a HEAD~ &&
+	test_write_lines >file1 local "" b &&
+	git commit -a -m "local update" &&
+	test_must_fail git merge test${test_count}_b &&
+	yes "" | git mergetool file1 &&
+	test_write_lines >expect local "" c &&
+	test_cmp expect file1 &&
+	git commit -m "test resolved with mergetool"
+'
+
 test_done
diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh
index 7c84a51..c0e9425 100755
--- a/t/t7611-merge-abort.sh
+++ b/t/t7611-merge-abort.sh
@@ -7,7 +7,7 @@
 There are three files foo/bar/baz, and the following graph illustrates the
 content of these files in each commit:
 
-# foo/bar/baz --- foo/bar/bazz     <-- master
+# foo/bar/baz --- foo/bar/bazz     <-- main
 #             \
 #              --- foo/barf/bazf   <-- conflict_branch
 #               \
@@ -22,6 +22,9 @@
 - changed/unchanged worktree after merge
 - changed/unchanged index after merge
 '
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -40,7 +43,7 @@
 	git checkout -b clean_branch HEAD^ &&
 	echo bart > bar &&
 	git commit -a -m "clean" &&
-	git checkout master
+	git checkout main
 '
 
 pre_merge_head="$(git rev-parse HEAD)"
diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh
index a426f3a..61330f7 100755
--- a/t/t7612-merge-verify-signatures.sh
+++ b/t/t7612-merge-verify-signatures.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='merge signature verification tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 
@@ -31,7 +34,7 @@
 	echo 3 >baz && git add baz &&
 	test_tick && git commit -SB7227189 -m "untrusted on side" &&
 
-	git checkout master
+	git checkout main
 '
 
 test_expect_success GPG 'merge unsigned commit with verification' '
diff --git a/t/t7614-merge-signoff.sh b/t/t7614-merge-signoff.sh
index c1b8446..fee258d 100755
--- a/t/t7614-merge-signoff.sh
+++ b/t/t7614-merge-signoff.sh
@@ -5,23 +5,26 @@
 This test runs git merge --signoff and makes sure that it works.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # Setup test files
 test_setup() {
 	# Expected commit message after merge --signoff
 	cat >expected-signed <<EOF &&
-Merge branch 'master' into other-branch
+Merge branch 'main' into other-branch
 
 Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
 EOF
 
 	# Expected commit message after merge without --signoff (or with --no-signoff)
 	cat >expected-unsigned <<EOF &&
-Merge branch 'master' into other-branch
+Merge branch 'main' into other-branch
 EOF
 
-	# Initial commit and feature branch to merge master into it.
+	# Initial commit and feature branch to merge main into it.
 	git commit --allow-empty -m "Initial empty commit" &&
 	git checkout -b other-branch &&
 	test_commit other-branch file1 1
@@ -38,30 +41,30 @@
 
 # Test with --signoff flag
 test_expect_success 'git merge --signoff adds a sign-off line' '
-	git checkout master &&
-	test_commit master-branch-2 file2 2 &&
+	git checkout main &&
+	test_commit main-branch-2 file2 2 &&
 	git checkout other-branch &&
-	git merge master --signoff --no-edit &&
+	git merge main --signoff --no-edit &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	test_cmp expected-signed actual
 '
 
 # Test without --signoff flag
 test_expect_success 'git merge does not add a sign-off line' '
-	git checkout master &&
-	test_commit master-branch-3 file3 3 &&
+	git checkout main &&
+	test_commit main-branch-3 file3 3 &&
 	git checkout other-branch &&
-	git merge master --no-edit &&
+	git merge main --no-edit &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	test_cmp expected-unsigned actual
 '
 
 # Test for --no-signoff flag
 test_expect_success 'git merge --no-signoff flag cancels --signoff flag' '
-	git checkout master &&
-	test_commit master-branch-4 file4 4 &&
+	git checkout main &&
+	test_commit main-branch-4 file4 4 &&
 	git checkout other-branch &&
-	git merge master --no-edit --signoff --no-signoff &&
+	git merge main --no-edit --signoff --no-signoff &&
 	git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
 	test_cmp expected-unsigned actual
 '
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index 48261ba..937f89e 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -2,6 +2,9 @@
 
 test_description='git repack works correctly'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 fsha1=
@@ -22,7 +25,7 @@
 	git commit -a -m more_content &&
 	csha1=$(git rev-parse HEAD^{commit}) &&
 	tsha1=$(git rev-parse HEAD^{tree}) &&
-	git checkout master &&
+	git checkout main &&
 	echo even more content >> file1 &&
 	test_tick &&
 	git commit -a -m even_more_content &&
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index a578b35..3e041e8 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -8,6 +8,9 @@
 Testing basic diff tool invocation
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 difftool_test_setup ()
@@ -35,23 +38,23 @@
 	test_i18ngrep ^usage: output
 '
 
-# Create a file on master and change it on branch
+# Create a file on main and change it on branch
 test_expect_success 'setup' '
-	echo master >file &&
+	echo main >file &&
 	git add file &&
 	git commit -m "added file" &&
 
-	git checkout -b branch master &&
+	git checkout -b branch main &&
 	echo branch >file &&
 	git commit -a -m "branch changed file" &&
-	git checkout master
+	git checkout main
 '
 
 # Configure a custom difftool.<tool>.cmd and use it
 test_expect_success 'custom commands' '
 	difftool_test_setup &&
 	test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" &&
-	echo master >expect &&
+	echo main >expect &&
 	git difftool --no-prompt branch >actual &&
 	test_cmp expect actual &&
 
@@ -63,7 +66,7 @@
 
 test_expect_success 'custom tool commands override built-ins' '
 	test_config difftool.vimdiff.cmd "cat \"\$REMOTE\"" &&
-	echo master >expect &&
+	echo main >expect &&
 	git difftool --tool vimdiff --no-prompt branch >actual &&
 	test_cmp expect actual
 '
@@ -311,21 +314,21 @@
 
 test_expect_success 'difftool --extcmd=cat' '
 	echo branch >expect &&
-	echo master >>expect &&
+	echo main >>expect &&
 	git difftool --no-prompt --extcmd=cat branch >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'difftool --extcmd cat' '
 	echo branch >expect &&
-	echo master >>expect &&
+	echo main >>expect &&
 	git difftool --no-prompt --extcmd=cat branch >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'difftool -x cat' '
 	echo branch >expect &&
-	echo master >>expect &&
+	echo main >>expect &&
 	git difftool --no-prompt -x cat branch >actual &&
 	test_cmp expect actual
 '
@@ -338,7 +341,7 @@
 '
 
 test_expect_success 'difftool --extcmd cat arg1' '
-	echo master >expect &&
+	echo main >expect &&
 	git difftool --no-prompt \
 		--extcmd sh\ -c\ \"cat\ \$1\" branch >actual &&
 	test_cmp expect actual
@@ -351,7 +354,7 @@
 	test_cmp expect actual
 '
 
-# Create a second file on master and a different version on branch
+# Create a second file on main and a different version on branch
 test_expect_success 'setup with 2 files different' '
 	echo m2 >file2 &&
 	git add file2 &&
@@ -361,7 +364,7 @@
 	echo br2 >file2 &&
 	git add file2 &&
 	git commit -a -m "branch changed file2" &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'say no to the first file' '
@@ -369,14 +372,14 @@
 	git difftool -x cat branch <input >output &&
 	grep m2 output &&
 	grep br2 output &&
-	! grep master output &&
+	! grep main output &&
 	! grep branch output
 '
 
 test_expect_success 'say no to the second file' '
 	(echo && echo n) >input &&
 	git difftool -x cat branch <input >output &&
-	grep master output &&
+	grep main output &&
 	grep branch output &&
 	! grep m2 output &&
 	! grep br2 output
@@ -384,7 +387,7 @@
 
 test_expect_success 'ending prompt input with EOF' '
 	git difftool -x cat branch </dev/null >output &&
-	! grep master output &&
+	! grep main output &&
 	! grep branch output &&
 	! grep m2 output &&
 	! grep br2 output
@@ -396,9 +399,9 @@
 '
 
 test_expect_success 'setup change in subdirectory' '
-	git checkout master &&
+	git checkout main &&
 	mkdir sub &&
-	echo master >sub/sub &&
+	echo main >sub/sub &&
 	git add sub/sub &&
 	git commit -m "added sub/sub" &&
 	git tag v1 &&
@@ -440,20 +443,20 @@
 
 run_dir_diff_test 'difftool -d' '
 	git difftool -d $symlinks --extcmd ls branch >output &&
-	grep sub output &&
-	grep file output
+	grep "^sub$" output &&
+	grep "^file$" output
 '
 
 run_dir_diff_test 'difftool --dir-diff' '
 	git difftool --dir-diff $symlinks --extcmd ls branch >output &&
-	grep sub output &&
-	grep file output
+	grep "^sub$" output &&
+	grep "^file$" output
 '
 
 run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
 	git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
-	grep sub output &&
-	grep file output
+	grep "^sub$" output &&
+	grep "^file$" output
 '
 
 run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
@@ -462,11 +465,11 @@
 		git difftool --dir-diff $symlinks --extcmd ls branch >output &&
 		# "sub" must only exist in "right"
 		# "file" and "file2" must be listed in both "left" and "right"
-		grep sub output >sub-output &&
+		grep "^sub$" output >sub-output &&
 		test_line_count = 1 sub-output &&
-		grep file"$" output >file-output &&
+		grep "^file$" output >file-output &&
 		test_line_count = 2 file-output &&
-		grep file2 output >file2-output &&
+		grep "^file2$" output >file2-output &&
 		test_line_count = 2 file2-output
 	)
 '
@@ -477,11 +480,11 @@
 		git difftool --dir-diff $symlinks --extcmd ls v1 >output &&
 		# "sub" and "file" exist in both v1 and HEAD.
 		# "file2" is unchanged.
-		grep sub output >sub-output &&
+		grep "^sub$" output >sub-output &&
 		test_line_count = 2 sub-output &&
-		grep file output >file-output &&
+		grep "^file$" output >file-output &&
 		test_line_count = 2 file-output &&
-		! grep file2 output
+		! grep "^file2$" output
 	)
 '
 
@@ -491,9 +494,9 @@
 		git difftool --dir-diff $symlinks --extcmd ls branch -- .>output &&
 		# "sub" only exists in "right"
 		# "file" and "file2" must not be listed
-		grep sub output >sub-output &&
+		grep "^sub$" output >sub-output &&
 		test_line_count = 1 sub-output &&
-		! grep file output
+		! grep "^file$" output
 	)
 '
 
@@ -503,9 +506,9 @@
 		git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output &&
 		# "sub" exists in v1 and HEAD
 		# "file" is filtered out by the pathspec
-		grep sub output >sub-output &&
+		grep "^sub$" output >sub-output &&
 		test_line_count = 2 sub-output &&
-		! grep file output
+		! grep "^file$" output
 	)
 '
 
@@ -518,16 +521,16 @@
 		cd sub &&
 		git difftool --dir-diff $symlinks --extcmd ls \
 			branch -- sub >output &&
-		grep sub output &&
-		! grep file output
+		grep "^sub$" output &&
+		! grep "^file$" output
 	)
 '
 
 run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
 	test_when_finished git reset --hard &&
 	rm file2 &&
-	git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
-	grep file2 output
+	git difftool --dir-diff $symlinks --extcmd ls branch main >output &&
+	grep "^file2$" output
 '
 
 run_dir_diff_test 'difftool --dir-diff with unmerged files' '
@@ -543,7 +546,7 @@
 	echo b >>file &&
 	git add file &&
 	git commit -m conflict-b &&
-	git checkout master &&
+	git checkout main &&
 	git merge conflict-a &&
 	test_must_fail git merge conflict-b &&
 	cat >expect <<-EOF &&
@@ -759,4 +762,36 @@
 	test_must_fail git difftool --gui --tool=test-tool --extcmd=cat
 '
 
+test_expect_success 'difftool --rotate-to' '
+	difftool_test_setup &&
+	test_when_finished git reset --hard &&
+	echo 1 >1 &&
+	echo 2 >2 &&
+	echo 4 >4 &&
+	git add 1 2 4 &&
+	git commit -a -m "124" &&
+	git difftool --no-prompt --extcmd=cat --rotate-to="2" HEAD^ >output&&
+	cat >expect <<-\EOF &&
+	2
+	4
+	1
+	EOF
+	test_cmp output expect
+'
+
+test_expect_success 'difftool --skip-to' '
+	difftool_test_setup &&
+	test_when_finished git reset --hard &&
+	git difftool --no-prompt --extcmd=cat --skip-to="2" HEAD^ >output &&
+	cat >expect <<-\EOF &&
+	2
+	4
+	EOF
+	test_cmp output expect
+'
+
+test_expect_success 'difftool --rotate/skip-to error condition' '
+	test_must_fail git difftool --no-prompt --extcmd=cat --rotate-to="3" HEAD^ &&
+	test_must_fail git difftool --no-prompt --extcmd=cat --skip-to="3" HEAD^
+'
 test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 991d5bd..edfaa9a 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -6,6 +6,9 @@
 test_description='git grep various.
 '
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 cat >hello.c <<EOF
@@ -687,21 +690,9 @@
 '
 
 test_expect_success 'log grep setup' '
-	echo a >>file &&
-	test_tick &&
-	GIT_AUTHOR_NAME="With * Asterisk" \
-	GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
-	git commit -a -m "second" &&
-
-	echo a >>file &&
-	test_tick &&
-	git commit -a -m "third" &&
-
-	echo a >>file &&
-	test_tick &&
-	GIT_AUTHOR_NAME="Night Fall" \
-	GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
-	git commit -a -m "fourth"
+	test_commit --append --author "With * Asterisk <xyzzy@frotz.com>" second file a &&
+	test_commit --append third file a &&
+	test_commit --append --author "Night Fall <nitfol@frobozz.com>" fourth file a
 '
 
 test_expect_success 'log grep (1)' '
@@ -978,7 +969,7 @@
 	"
 done
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'grep --threads=N or pack.threads=N warns when no pthreads' '
 	git grep --threads=2 Hello hello_world 2>err &&
 	grep ^warning: err >warnings &&
 	test_line_count = 1 warnings &&
@@ -1206,19 +1197,19 @@
 '
 
 test_expect_success 'dashdash disambiguates rev as rev' '
-	test_when_finished "rm -f master" &&
-	echo content >master &&
-	echo master:hello.c >expect &&
-	git grep -l o master -- hello.c >actual &&
+	test_when_finished "rm -f main" &&
+	echo content >main &&
+	echo main:hello.c >expect &&
+	git grep -l o main -- hello.c >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'dashdash disambiguates pathspec as pathspec' '
-	test_when_finished "git rm -f master" &&
-	echo content >master &&
-	git add master &&
-	echo master:content >expect &&
-	git grep o -- master >actual &&
+	test_when_finished "git rm -f main" &&
+	echo content >main &&
+	git add main &&
+	echo main:content >expect &&
+	git grep o -- main >actual &&
 	test_cmp expect actual
 '
 
@@ -1254,15 +1245,15 @@
 '
 
 test_expect_success 'grep --no-index complains of revs' '
-	test_must_fail git grep --no-index o master -- 2>err &&
+	test_must_fail git grep --no-index o main -- 2>err &&
 	test_i18ngrep "cannot be used with revs" err
 '
 
 test_expect_success 'grep --no-index prefers paths to revs' '
-	test_when_finished "rm -f master" &&
-	echo content >master &&
-	echo master:content >expect &&
-	git grep --no-index o master >actual &&
+	test_when_finished "rm -f main" &&
+	echo content >main &&
+	echo main:content >expect &&
+	git grep --no-index o main >actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh
index 03dba66..e5d1e4e 100755
--- a/t/t7812-grep-icase-non-ascii.sh
+++ b/t/t7812-grep-icase-non-ascii.sh
@@ -57,7 +57,12 @@
 	printf "\\200\\n" >invalid-0x80 &&
 	echo "ævar" >expected &&
 	cat expected >>invalid-0x80 &&
-	git add invalid-0x80
+	git add invalid-0x80 &&
+
+	# Test for PCRE2_MATCH_INVALID_UTF bug
+	# https://bugs.exim.org/show_bug.cgi?id=2642
+	printf "\\345Aæ\\n" >invalid-0xe5 &&
+	git add invalid-0xe5
 '
 
 test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep ASCII from invalid UTF-8 data' '
@@ -67,6 +72,13 @@
 	test_cmp expected actual
 '
 
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep ASCII from invalid UTF-8 data (PCRE2 bug #2642)' '
+	git grep -h "Aæ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual &&
+	git grep -h "(*NO_JIT)Aæ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual
+'
+
 test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-ASCII from invalid UTF-8 data' '
 	git grep -h "æ" invalid-0x80 >actual &&
 	test_cmp expected actual &&
@@ -74,14 +86,41 @@
 	test_cmp expected actual
 '
 
+test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-ASCII from invalid UTF-8 data (PCRE2 bug #2642)' '
+	git grep -h "Aæ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual &&
+	git grep -h "(*NO_JIT)Aæ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual
+'
+
+test_lazy_prereq PCRE2_MATCH_INVALID_UTF '
+	test-tool pcre2-config has-PCRE2_MATCH_INVALID_UTF
+'
+
 test_expect_success GETTEXT_LOCALE,LIBPCRE2 'PCRE v2: grep non-ASCII from invalid UTF-8 data with -i' '
 	test_might_fail git grep -hi "Æ" invalid-0x80 >actual &&
-	if test -s actual
-	then
-	    test_cmp expected actual
-	fi &&
-	test_must_fail git grep -hi "(*NO_JIT)Æ" invalid-0x80 >actual &&
-	! test_cmp expected actual
+	test_might_fail git grep -hi "(*NO_JIT)Æ" invalid-0x80 >actual
+'
+
+test_expect_success GETTEXT_LOCALE,LIBPCRE2,PCRE2_MATCH_INVALID_UTF 'PCRE v2: grep non-ASCII from invalid UTF-8 data with -i' '
+	git grep -hi "Æ" invalid-0x80 >actual &&
+	test_cmp expected actual &&
+	git grep -hi "(*NO_JIT)Æ" invalid-0x80 >actual &&
+	test_cmp expected actual
+'
+
+test_expect_success GETTEXT_LOCALE,LIBPCRE2,PCRE2_MATCH_INVALID_UTF 'PCRE v2: grep non-ASCII from invalid UTF-8 data with -i (PCRE2 bug #2642)' '
+	git grep -hi "Æ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual &&
+	git grep -hi "(*NO_JIT)Æ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual &&
+
+	# Only the case of grepping the ASCII part in a way that
+	# relies on -i fails
+	git grep -hi "aÆ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual &&
+	git grep -hi "(*NO_JIT)aÆ" invalid-0xe5 >actual &&
+	test_cmp invalid-0xe5 actual
 '
 
 test_done
diff --git a/t/t7817-grep-sparse-checkout.sh b/t/t7817-grep-sparse-checkout.sh
new file mode 100755
index 0000000..590b99b
--- /dev/null
+++ b/t/t7817-grep-sparse-checkout.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+test_description='grep in sparse checkout
+
+This test creates a repo with the following structure:
+
+.
+|-- a
+|-- b
+|-- dir
+|   `-- c
+|-- sub
+|   |-- A
+|   |   `-- a
+|   `-- B
+|       `-- b
+`-- sub2
+    `-- a
+
+Where the outer repository has non-cone mode sparsity patterns, sub is a
+submodule with cone mode sparsity patterns and sub2 is a submodule that is
+excluded by the superproject sparsity patterns. The resulting sparse checkout
+should leave the following structure in the working tree:
+
+.
+|-- a
+|-- sub
+|   `-- B
+|       `-- b
+`-- sub2
+    `-- a
+
+But note that sub2 should have the SKIP_WORKTREE bit set.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo "text" >a &&
+	echo "text" >b &&
+	mkdir dir &&
+	echo "text" >dir/c &&
+
+	git init sub &&
+	(
+		cd sub &&
+		mkdir A B &&
+		echo "text" >A/a &&
+		echo "text" >B/b &&
+		git add A B &&
+		git commit -m sub &&
+		git sparse-checkout init --cone &&
+		git sparse-checkout set B
+	) &&
+
+	git init sub2 &&
+	(
+		cd sub2 &&
+		echo "text" >a &&
+		git add a &&
+		git commit -m sub2
+	) &&
+
+	git submodule add ./sub &&
+	git submodule add ./sub2 &&
+	git add a b dir &&
+	git commit -m super &&
+	git sparse-checkout init --no-cone &&
+	git sparse-checkout set "/*" "!b" "!/*/" "sub" &&
+
+	git tag -am tag-to-commit tag-to-commit HEAD &&
+	tree=$(git rev-parse HEAD^{tree}) &&
+	git tag -am tag-to-tree tag-to-tree $tree &&
+
+	test_path_is_missing b &&
+	test_path_is_missing dir &&
+	test_path_is_missing sub/A &&
+	test_path_is_file a &&
+	test_path_is_file sub/B/b &&
+	test_path_is_file sub2/a &&
+	git branch -m main
+'
+
+# The test below covers a special case: the sparsity patterns exclude '/b' and
+# sparse checkout is enabled, but the path exists in the working tree (e.g.
+# manually created after `git sparse-checkout init`). git grep should skip it.
+test_expect_success 'working tree grep honors sparse checkout' '
+	cat >expect <<-EOF &&
+	a:text
+	EOF
+	test_when_finished "rm -f b" &&
+	echo "new-text" >b &&
+	git grep "text" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep searches unmerged file despite not matching sparsity patterns' '
+	cat >expect <<-EOF &&
+	b:modified-b-in-branchX
+	b:modified-b-in-branchY
+	EOF
+	test_when_finished "test_might_fail git merge --abort && \
+			    git checkout main && git sparse-checkout init" &&
+
+	git sparse-checkout disable &&
+	git checkout -b branchY main &&
+	test_commit modified-b-in-branchY b &&
+	git checkout -b branchX main &&
+	test_commit modified-b-in-branchX b &&
+
+	git sparse-checkout init &&
+	test_path_is_missing b &&
+	test_must_fail git merge branchY &&
+	git grep "modified-b" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit' '
+	cat >expect <<-EOF &&
+	a:text
+	b:text
+	dir/c:text
+	EOF
+	git grep --cached "text" >actual &&
+	test_cmp expect actual
+'
+
+# Note that sub2/ is present in the worktree but it is excluded by the sparsity
+# patterns, so grep should not recurse into it.
+test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' '
+	cat >expect <<-EOF &&
+	a:text
+	sub/B/b:text
+	EOF
+	git grep --recurse-submodules "text" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep --recurse-submodules --cached searches entries with the SKIP_WORKTREE bit' '
+	cat >expect <<-EOF &&
+	a:text
+	b:text
+	dir/c:text
+	sub/A/a:text
+	sub/B/b:text
+	sub2/a:text
+	EOF
+	git grep --recurse-submodules --cached "text" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'working tree grep does not search the index with CE_VALID and SKIP_WORKTREE' '
+	cat >expect <<-EOF &&
+	a:text
+	EOF
+	test_when_finished "git update-index --no-assume-unchanged b" &&
+	git update-index --assume-unchanged b &&
+	git grep text >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep --cached searches index entries with both CE_VALID and SKIP_WORKTREE' '
+	cat >expect <<-EOF &&
+	a:text
+	b:text
+	dir/c:text
+	EOF
+	test_when_finished "git update-index --no-assume-unchanged b" &&
+	git update-index --assume-unchanged b &&
+	git grep --cached text >actual &&
+	test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 99bf0c7..2412d8c 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -7,6 +7,19 @@
 GIT_TEST_COMMIT_GRAPH=0
 GIT_TEST_MULTI_PACK_INDEX=0
 
+test_lazy_prereq XMLLINT '
+	xmllint --version
+'
+
+test_xmllint () {
+	if test_have_prereq XMLLINT
+	then
+		xmllint --noout "$@"
+	else
+		true
+	fi
+}
+
 test_expect_success 'help text' '
 	test_expect_code 129 git maintenance -h 2>err &&
 	test_i18ngrep "usage: git maintenance <subcommand>" err &&
@@ -136,7 +149,31 @@
 	git log prefetch/remote2/two &&
 	git fetch --all &&
 	test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
-	test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two
+	test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two &&
+
+	test_cmp_config refs/prefetch/ log.excludedecoration &&
+	git log --oneline --decorate --all >log &&
+	! grep "prefetch" log
+'
+
+test_expect_success 'prefetch and existing log.excludeDecoration values' '
+	git config --unset-all log.excludeDecoration &&
+	git config log.excludeDecoration refs/remotes/remote1/ &&
+	git maintenance run --task=prefetch &&
+
+	git config --get-all log.excludeDecoration >out &&
+	grep refs/remotes/remote1/ out &&
+	grep refs/prefetch/ out &&
+
+	git log --oneline --decorate --all >log &&
+	! grep "prefetch" log &&
+	! grep "remote1" log &&
+	grep "remote2" log &&
+
+	# a second run does not change the config
+	git maintenance run --task=prefetch &&
+	git log --oneline --decorate --all >log2 &&
+	test_cmp log log2
 '
 
 test_expect_success 'loose-objects task' '
@@ -219,6 +256,13 @@
 	HEAD
 	^HEAD~1
 	EOF
+
+	# Delete refs that have not been repacked in these packs.
+	git for-each-ref --format="delete %(refname)" \
+		refs/prefetch refs/tags >refs &&
+	git update-ref --stdin <refs &&
+
+	# Replace the object directory with this pack layout.
 	rm -f $packDir/pack-* &&
 	rm -f $packDir/loose-* &&
 	ls $packDir/*.pack >packs-before &&
@@ -299,6 +343,18 @@
 	test_subcommand git multi-pack-index write --no-progress <trace-B
 '
 
+test_expect_success 'pack-refs task' '
+	for n in $(test_seq 1 5)
+	do
+		git branch -f to-pack/$n HEAD || return 1
+	done &&
+	GIT_TRACE2_EVENT="$(pwd)/pack-refs.txt" \
+		git maintenance run --task=pack-refs &&
+	ls .git/refs/heads/ >after &&
+	test_must_be_empty after &&
+	test_subcommand git pack-refs --all --prune <pack-refs.txt
+'
+
 test_expect_success '--auto and --schedule incompatible' '
 	test_must_fail git maintenance run --auto --schedule=daily 2>err &&
 	test_i18ngrep "at most one" err
@@ -352,18 +408,32 @@
 		git maintenance run --schedule=hourly --quiet &&
 	GIT_TRACE2_EVENT="$(pwd)/incremental-daily.txt" \
 		git maintenance run --schedule=daily --quiet &&
+	GIT_TRACE2_EVENT="$(pwd)/incremental-weekly.txt" \
+		git maintenance run --schedule=weekly --quiet &&
 
 	test_subcommand git commit-graph write --split --reachable \
 		--no-progress <incremental-hourly.txt &&
 	test_subcommand ! git prune-packed --quiet <incremental-hourly.txt &&
 	test_subcommand ! git multi-pack-index write --no-progress \
 		<incremental-hourly.txt &&
+	test_subcommand ! git pack-refs --all --prune \
+		<incremental-hourly.txt &&
 
 	test_subcommand git commit-graph write --split --reachable \
 		--no-progress <incremental-daily.txt &&
 	test_subcommand git prune-packed --quiet <incremental-daily.txt &&
 	test_subcommand git multi-pack-index write --no-progress \
 		<incremental-daily.txt &&
+	test_subcommand ! git pack-refs --all --prune \
+		<incremental-daily.txt &&
+
+	test_subcommand git commit-graph write --split --reachable \
+		--no-progress <incremental-weekly.txt &&
+	test_subcommand git prune-packed --quiet <incremental-weekly.txt &&
+	test_subcommand git multi-pack-index write --no-progress \
+		<incremental-weekly.txt &&
+	test_subcommand git pack-refs --all --prune \
+		<incremental-weekly.txt &&
 
 	# Modify defaults
 	git config maintenance.commit-graph.schedule daily &&
@@ -419,7 +489,7 @@
 '
 
 test_expect_success 'start from empty cron table' '
-	GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
+	GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
 
 	# start registers the repo
 	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
@@ -430,19 +500,19 @@
 '
 
 test_expect_success 'stop from existing schedule' '
-	GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
+	GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
 
 	# stop does not unregister the repo
 	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
 
 	# Operation is idempotent
-	GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
+	GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
 	test_must_be_empty cron.txt
 '
 
 test_expect_success 'start preserves existing schedule' '
 	echo "Important information!" >cron.txt &&
-	GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance start &&
+	GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance start &&
 	grep "Important information!" cron.txt
 '
 
@@ -457,11 +527,94 @@
 
 test_expect_success 'stop preserves surrounding schedule' '
 	echo "Crucial information!" >>cron.txt &&
-	GIT_TEST_CRONTAB="test-tool crontab cron.txt" git maintenance stop &&
+	GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt" git maintenance stop &&
 	grep "Important information!" cron.txt &&
 	grep "Crucial information!" cron.txt
 '
 
+test_expect_success 'start and stop macOS maintenance' '
+	# ensure $HOME can be compared against hook arguments on all platforms
+	pfx=$(cd "$HOME" && pwd) &&
+
+	write_script print-args <<-\EOF &&
+	echo $* | sed "s:gui/[0-9][0-9]*:gui/[UID]:" >>args
+	EOF
+
+	rm -f args &&
+	GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+
+	# start registers the repo
+	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+	ls "$HOME/Library/LaunchAgents" >actual &&
+	cat >expect <<-\EOF &&
+	org.git-scm.git.daily.plist
+	org.git-scm.git.hourly.plist
+	org.git-scm.git.weekly.plist
+	EOF
+	test_cmp expect actual &&
+
+	rm -f expect &&
+	for frequency in hourly daily weekly
+	do
+		PLIST="$pfx/Library/LaunchAgents/org.git-scm.git.$frequency.plist" &&
+		test_xmllint "$PLIST" &&
+		grep schedule=$frequency "$PLIST" &&
+		echo "bootout gui/[UID] $PLIST" >>expect &&
+		echo "bootstrap gui/[UID] $PLIST" >>expect || return 1
+	done &&
+	test_cmp expect args &&
+
+	rm -f args &&
+	GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance stop &&
+
+	# stop does not unregister the repo
+	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+	printf "bootout gui/[UID] $pfx/Library/LaunchAgents/org.git-scm.git.%s.plist\n" \
+		hourly daily weekly >expect &&
+	test_cmp expect args &&
+	ls "$HOME/Library/LaunchAgents" >actual &&
+	test_line_count = 0 actual
+'
+
+test_expect_success 'start and stop Windows maintenance' '
+	write_script print-args <<-\EOF &&
+	echo $* >>args
+	while test $# -gt 0
+	do
+		case "$1" in
+		/xml) shift; xmlfile=$1; break ;;
+		*) shift ;;
+		esac
+	done
+	test -z "$xmlfile" || cp "$xmlfile" "$xmlfile.xml"
+	EOF
+
+	rm -f args &&
+	GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance start &&
+
+	# start registers the repo
+	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+	for frequency in hourly daily weekly
+	do
+		grep "/create /tn Git Maintenance ($frequency) /f /xml" args &&
+		file=$(ls .git/schedule_${frequency}*.xml) &&
+		test_xmllint "$file" || return 1
+	done &&
+
+	rm -f args &&
+	GIT_TEST_MAINT_SCHEDULER="schtasks:./print-args" git maintenance stop &&
+
+	# stop does not unregister the repo
+	git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+
+	printf "/delete /tn Git Maintenance (%s) /f\n" \
+		hourly daily weekly >expect &&
+	test_cmp expect args
+'
+
 test_expect_success 'register preserves existing strategy' '
 	git config maintenance.strategy none &&
 	git maintenance register &&
@@ -479,4 +632,17 @@
 	nongit test_must_fail git maintenance unregister
 '
 
+test_expect_success 'register and unregister bare repo' '
+	test_when_finished "git config --global --unset-all maintenance.repo || :" &&
+	test_might_fail git config --global --unset-all maintenance.repo &&
+	git init --bare barerepo &&
+	(
+		cd barerepo &&
+		git maintenance register &&
+		git config --get --global --fixed-value maintenance.repo "$(pwd)" &&
+		git maintenance unregister &&
+		test_must_fail git config --global --get-all maintenance.repo
+	)
+'
+
 test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 72176e4..a536a62 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -1,13 +1,16 @@
 #!/bin/sh
 
 test_description='git annotate'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 PROG='git annotate'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_expect_success 'annotate old revision' '
-	git annotate file master >actual &&
+	git annotate file main >actual &&
 	awk "{ print \$3; }" <actual >authors &&
 	test 2 = $(grep A <authors | wc -l) &&
 	test 2 = $(grep B <authors | wc -l)
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 015973e..5bb302b 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git blame'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 PROG='git blame -c'
diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh
index ba8013b..da80f81 100755
--- a/t/t8003-blame-corner-cases.sh
+++ b/t/t8003-blame-corner-cases.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git blame corner cases'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
@@ -161,13 +164,13 @@
 
 test_expect_success 'blame during cherry-pick with file rename conflict' '
 
-	test_when_finished "git reset --hard && git checkout master" &&
+	test_when_finished "git reset --hard && git checkout main" &&
 	git checkout HEAD~3 &&
 	echo MOUSE >> mouse &&
 	git mv mouse rodent &&
 	git add rodent &&
 	GIT_AUTHOR_NAME=Rodent git commit -m "rodent" &&
-	git checkout --detach master &&
+	git checkout --detach main &&
 	(git cherry-pick HEAD@{1} || test $? -eq 1) &&
 	git show HEAD@{1}:rodent > rodent &&
 	git add rodent &&
diff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh
index 9c353ab..35414a5 100755
--- a/t/t8004-blame-with-conflicts.sh
+++ b/t/t8004-blame-with-conflicts.sh
@@ -3,6 +3,9 @@
 # Based on a test case submitted by Björn Steinbrink.
 
 test_description='git blame on conflicted files'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup first case' '
@@ -21,16 +24,16 @@
 	git add file2 &&
 	git commit --author "U Gly <ug@localhost>" -m ugly &&
 
-	# Back to master and change something
-	git checkout master &&
+	# Back to main and change something
+	git checkout main &&
 	echo "
 
 bla" >> file1 &&
 	git commit --author "Old Line <ol@localhost>" -a -m file1.b &&
 
-	# Back to foo and merge master
+	# Back to foo and merge main
 	git checkout foo &&
-	if git merge master; then
+	if git merge main; then
 		echo needed conflict here
 		exit 1
 	else
@@ -44,8 +47,8 @@
 	git rm file1 &&
 	git commit --author "M Result <mr@localhost>" -a -m merged &&
 
-	# Back to master and change file1 again
-	git checkout master &&
+	# Back to main and change file1 again
+	git checkout main &&
 	sed s/bla/foo/ <file1 >X &&
 	rm file1 &&
 	mv X file1 &&
@@ -53,7 +56,7 @@
 
 	# Try to merge into foo again
 	git checkout foo &&
-	if git merge master; then
+	if git merge main; then
 		echo needed conflict here
 		exit 1
 	else
diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh
index ed38f74..90c75db 100755
--- a/t/t8012-blame-colors.sh
+++ b/t/t8012-blame-colors.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='colored git blame'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 PROG='git blame -c'
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index a08f725..4eee9c3 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git send-email'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 # May be altered later in the test
@@ -1168,10 +1171,10 @@
 '
 
 test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
-	echo master >master &&
-	git add master &&
-	git commit -m"add master" &&
-	test_must_fail git send-email --dry-run master 2>errors &&
+	echo main >main &&
+	git add main &&
+	git commit -m"add main" &&
+	test_must_fail git send-email --dry-run main 2>errors &&
 	grep disambiguate errors
 '
 
@@ -1185,7 +1188,7 @@
 		outdir/000?-*.patch 2>errors >out &&
 	grep "^Subject: " out >subjects &&
 	test "z$(sed -n -e 1p subjects)" = "zSubject: [PATCH 1/2] Second." &&
-	test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
+	test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add main"
 '
 
 test_expect_success $PREREQ 'in-reply-to but no threading' '
@@ -2037,7 +2040,7 @@
 	--cc="Cc2 <cc2@example.com>" \
 	--bcc="bcc1@example.com" \
 	--bcc="bcc2@example.com" \
-	0001-add-master.patch | replace_variable_fields \
+	0001-add-main.patch | replace_variable_fields \
 	>expected-list
 '
 
@@ -2049,7 +2052,7 @@
 	--to="to3@example.com" \
 	--cc="Cc 1 <cc1@example.com>, Cc2 <cc2@example.com>" \
 	--bcc="bcc1@example.com, bcc2@example.com" \
-	0001-add-master.patch | replace_variable_fields \
+	0001-add-main.patch | replace_variable_fields \
 	>actual-list &&
 	test_cmp expected-list actual-list
 '
@@ -2065,7 +2068,7 @@
 	--to="To 1 <to1@example.com>, to2, to3@example.com" \
 	--cc="cc1, Cc2 <cc2@example.com>" \
 	--bcc="bcc1@example.com, bcc2@example.com" \
-	0001-add-master.patch | replace_variable_fields \
+	0001-add-main.patch | replace_variable_fields \
 	>actual-list &&
 	test_cmp expected-list actual-list
 '
@@ -2089,7 +2092,7 @@
 	--cc="Cc2 <cc2@example.com>" \
 	--bcc="$BCC1" \
 	--bcc="bcc2@example.com" \
-	0001-add-master.patch | replace_variable_fields \
+	0001-add-main.patch | replace_variable_fields \
 	>actual-list &&
 	test_cmp expected-list actual-list
 '
@@ -2108,8 +2111,8 @@
 		false
 		;;
 	esac &&
-	test -f 0001-add-master.patch &&
-	grep "add master" "$1"
+	test -f 0001-add-main.patch &&
+	grep "add main" "$1"
 	EOF
 
 	mkdir subdir &&
@@ -2121,10 +2124,10 @@
 			--from="Example <nobody@example.com>" \
 			--to=nobody@example.com \
 			--smtp-server="$(pwd)/../fake.sendmail" \
-			../0001-add-master.patch &&
+			../0001-add-main.patch &&
 
 		# Verify error message when a patch is rejected by the hook
-		sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
+		sed -e "s/add main/x/" ../0001-add-main.patch >../another.patch &&
 		test_must_fail git send-email \
 			--from="Example <nobody@example.com>" \
 			--to=nobody@example.com \
@@ -2139,7 +2142,7 @@
 		--from="Example <nobody@example.com>" \
 		--to=nobody@example.com \
 		--smtp-server="$(pwd)/fake.sendmail" \
-		"$(pwd)/0001-add-master.patch"
+		"$(pwd)/0001-add-main.patch"
 '
 
 test_expect_success $PREREQ 'test that sendmail config is rejected' '
diff --git a/t/t9003-help-autocorrect.sh b/t/t9003-help-autocorrect.sh
index 03cd5c5..f00deaf 100755
--- a/t/t9003-help-autocorrect.sh
+++ b/t/t9003-help-autocorrect.sh
@@ -56,11 +56,8 @@
 	git config help.autocorrect never &&
 
 	test_must_fail git lfg 2>actual &&
-	if test_have_prereq C_LOCALE_OUTPUT
-	then
-		grep "is not a git command" actual &&
-		test_line_count = 1 actual
-	fi
+	grep "is not a git command" actual &&
+	test_line_count = 1 actual
 '
 
 test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index e4bb220..1d3fdcc 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -6,6 +6,9 @@
 test_description='git svn basic tests'
 GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 case "$GIT_SVN_LC_ALL" in
@@ -275,7 +278,7 @@
 	test refs/heads/my-bar = $(git symbolic-ref HEAD) &&
 	git log refs/remotes/bar | grep "change 1" &&
 	! git log refs/remotes/bar | grep "change 2" &&
-	git checkout master &&
+	git checkout main &&
 	git branch -D my-bar
 	'
 
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
index 3bbf341..f863e43 100755
--- a/t/t9145-git-svn-master-branch.sh
+++ b/t/t9145-git-svn-master-branch.sh
@@ -2,7 +2,10 @@
 #
 # Copyright (c) 2009 Eric Wong
 #
-test_description='git svn initial master branch is "trunk" if possible'
+test_description='git svn initial main branch is "trunk" if possible'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 test_expect_success 'setup test repository' '
@@ -13,12 +16,12 @@
 	svn_cmd import -m b/b i "$svnrepo/branches/b"
 '
 
-test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+test_expect_success 'git svn clone --stdlayout sets up trunk as main' '
 	git svn clone -s "$svnrepo" g &&
 	(
 		cd g &&
 		test x$(git rev-parse --verify refs/remotes/origin/trunk^0) = \
-		     x$(git rev-parse --verify refs/heads/master^0)
+		     x$(git rev-parse --verify refs/heads/main^0)
 	)
 '
 
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 4f6c06e..1fbe84f 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,53 +5,53 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
 	svnadmin load -q '$rawsvnrepo' \
-	  < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
+	  <'$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
 	git svn init --minimize-url -R svnmerge \
 	  --rewrite-root=http://svn.example.org \
 	  -T trunk -b branches '$svnrepo' &&
 	git svn fetch --all
-	"
+"
 
 test_expect_success 'all svn merges became git merge commits' '
-	unmarked=$(git rev-list --parents --all --grep=Merge |
-		grep -v " .* " | cut -f1 -d" ") &&
-	[ -z "$unmarked" ]
-	'
+	git rev-list --all --no-merges --grep=Merge >unmarked &&
+	test_must_be_empty unmarked
+'
 
 test_expect_success 'cherry picks did not become git merge commits' '
-	bad_cherries=$(git rev-list --parents --all --grep=Cherry |
-		grep " .* " | cut -f1 -d" ") &&
-	[ -z "$bad_cherries" ]
-	'
+	git rev-list --all --merges --grep=Cherry >bad-cherries &&
+	test_must_be_empty bad-cherries
+'
 
 test_expect_success 'svn non-merge merge commits did not become git merge commits' '
-	bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
-		grep " .* " | cut -f1 -d" ") &&
-	[ -z "$bad_non_merges" ]
-	'
+	git rev-list --all --merges --grep=non-merge >bad-non-merges &&
+	test_must_be_empty bad-non-merges
+'
 
 test_expect_success 'commit made to merged branch is reachable from the merge' '
 	before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2") &&
 	merge_commit=$(git rev-list --all --grep="Merge trunk to b2") &&
-	not_reachable=$(git rev-list -1 $before_commit --not $merge_commit) &&
-	[ -z "$not_reachable" ]
-	'
+	git rev-list -1 $before_commit --not $merge_commit >not-reachable &&
+	test_must_be_empty not-reachable
+'
 
 test_expect_success 'merging two branches in one commit is detected correctly' '
 	f1_commit=$(git rev-list --all --grep="make f1 branch from trunk") &&
 	f2_commit=$(git rev-list --all --grep="make f2 branch from trunk") &&
 	merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk") &&
-	not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit) &&
-	[ -z "$not_reachable" ]
-	'
+	git rev-list -1 $f1_commit $f2_commit --not $merge_commit >not-reachable &&
+	test_must_be_empty not-reachable
+'
 
 test_expect_failure 'everything got merged in the end' '
-	unmerged=$(git rev-list --all --not master) &&
-	[ -z "$unmerged" ]
-	'
+	git rev-list --all --not main >unmerged &&
+	test_must_be_empty unmerged
+'
 
 test_done
diff --git a/t/t9155-git-svn-fetch-deleted-tag.sh b/t/t9155-git-svn-fetch-deleted-tag.sh
index 184336f..3258374 100755
--- a/t/t9155-git-svn-fetch-deleted-tag.sh
+++ b/t/t9155-git-svn-fetch-deleted-tag.sh
@@ -2,6 +2,9 @@
 
 test_description='git svn fetch deleted tag'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repo' '
@@ -36,7 +39,7 @@
 	git svn fetch &&
 
 	git diff --exit-code origin/mybranch:trunk/subdir/file origin/tags/mytag:file &&
-	git diff --exit-code master:subdir/file origin/tags/mytag^:file
+	git diff --exit-code main:subdir/file origin/tags/mytag^:file
 '
 
 test_done
diff --git a/t/t9156-git-svn-fetch-deleted-tag-2.sh b/t/t9156-git-svn-fetch-deleted-tag-2.sh
index 7a6e33b..2b75c40 100755
--- a/t/t9156-git-svn-fetch-deleted-tag-2.sh
+++ b/t/t9156-git-svn-fetch-deleted-tag-2.sh
@@ -2,6 +2,9 @@
 
 test_description='git svn fetch deleted tag 2'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repo' '
@@ -36,9 +39,9 @@
 	cd git_project &&
 	git svn fetch &&
 
-	git diff --exit-code master:subdir3/file origin/tags/mytag:file &&
-	git diff --exit-code master:subdir2/file origin/tags/mytag^:file &&
-	git diff --exit-code master:subdir1/file origin/tags/mytag^^:file
+	git diff --exit-code main:subdir3/file origin/tags/mytag:file &&
+	git diff --exit-code main:subdir2/file origin/tags/mytag^:file &&
+	git diff --exit-code main:subdir1/file origin/tags/mytag^^:file
 '
 
 test_done
diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
index d6245ce..978eb62 100755
--- a/t/t9163-git-svn-reset-clears-caches.sh
+++ b/t/t9163-git-svn-reset-clears-caches.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='git svn reset clears memoized caches'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
@@ -59,7 +62,7 @@
 test_expect_success 'rebase looses SVN merge (m)' '
 	git svn rebase &&
 	git svn fetch &&
-	test 1 = $(git cat-file -p master|grep parent|wc -l)
+	test 1 = $(git cat-file -p main|grep parent|wc -l)
 '
 
 # git svn fetch creates correct history with merge commit
diff --git a/t/t9169-git-svn-dcommit-crlf.sh b/t/t9169-git-svn-dcommit-crlf.sh
index 54b1f61..7080b5d 100755
--- a/t/t9169-git-svn-dcommit-crlf.sh
+++ b/t/t9169-git-svn-dcommit-crlf.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git svn dcommit CRLF'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-svn.sh
 
 test_expect_success 'setup commit repository' '
@@ -14,7 +17,7 @@
 		p=$(git rev-parse HEAD) &&
 		t=$(git write-tree) &&
 		cmt=$(git commit-tree -p $p $t <cmt) &&
-		git update-ref refs/heads/master $cmt &&
+		git update-ref refs/heads/main $cmt &&
 		git cat-file commit HEAD | tail -n4 >out &&
 		test_cmp cmt out &&
 		git svn dcommit &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 308c1ef..5c47ac4 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -4,8 +4,11 @@
 #
 
 test_description='test git fast-import utility'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 verify_packs () {
 	for p in .git/objects/pack/*.pack
@@ -62,7 +65,7 @@
 	mark :4
 	data $file4_len
 	$file4_data
-	commit refs/heads/master
+	commit refs/heads/main
 	mark :5
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	data <<COMMIT
@@ -117,7 +120,7 @@
 
 	INPUT_END
 	git fast-import --export-marks=marks.out <input &&
-	git whatchanged master
+	git whatchanged main
 '
 
 test_expect_success 'A: verify pack' '
@@ -131,7 +134,7 @@
 
 	initial
 	EOF
-	git cat-file commit master | sed 1d >actual &&
+	git cat-file commit main | sed 1d >actual &&
 	test_cmp expect actual
 '
 
@@ -141,31 +144,31 @@
 	100644 blob file3
 	100755 blob file4
 	EOF
-	git cat-file -p master^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
+	git cat-file -p main^{tree} | sed "s/ [0-9a-f]*	/ /" >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'A: verify file2' '
 	echo "$file2_data" >expect &&
-	git cat-file blob master:file2 >actual &&
+	git cat-file blob main:file2 >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'A: verify file3' '
 	echo "$file3_data" >expect &&
-	git cat-file blob master:file3 >actual &&
+	git cat-file blob main:file3 >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'A: verify file4' '
 	printf "$file4_data" >expect &&
-	git cat-file blob master:file4 >actual &&
+	git cat-file blob main:file4 >actual &&
 	test_cmp expect actual
 '
 
 test_expect_success 'A: verify tag/series-A' '
 	cat >expect <<-EOF &&
-	object $(git rev-parse refs/heads/master)
+	object $(git rev-parse refs/heads/main)
 	type commit
 	tag series-A
 
@@ -177,7 +180,7 @@
 
 test_expect_success 'A: verify tag/series-A-blob' '
 	cat >expect <<-EOF &&
-	object $(git rev-parse refs/heads/master:file3)
+	object $(git rev-parse refs/heads/main:file3)
 	type blob
 	tag series-A-blob
 
@@ -193,13 +196,13 @@
 
 test_expect_success 'A: verify marks output' '
 	cat >expect <<-EOF &&
-	:2 $(git rev-parse --verify master:file2)
-	:3 $(git rev-parse --verify master:file3)
-	:4 $(git rev-parse --verify master:file4)
-	:5 $(git rev-parse --verify master^0)
+	:2 $(git rev-parse --verify main:file2)
+	:3 $(git rev-parse --verify main:file3)
+	:4 $(git rev-parse --verify main:file4)
+	:5 $(git rev-parse --verify main^0)
 	:6 $(git cat-file tag nested | grep object | cut -d" " -f 2)
 	:7 $(git rev-parse --verify nested)
-	:8 $(git rev-parse --verify master^0)
+	:8 $(git rev-parse --verify main^0)
 	EOF
 	test_cmp expect marks.out
 '
@@ -217,7 +220,7 @@
 	new_blob=$(echo testing | git hash-object --stdin) &&
 	cat >input <<-INPUT_END &&
 	tag series-A-blob-2
-	from $(git rev-parse refs/heads/master:file3)
+	from $(git rev-parse refs/heads/main:file3)
 	data <<EOF
 	Tag blob by sha1.
 	EOF
@@ -243,7 +246,7 @@
 	INPUT_END
 
 	cat >expect <<-EOF &&
-	object $(git rev-parse refs/heads/master:file3)
+	object $(git rev-parse refs/heads/main:file3)
 	type blob
 	tag series-A-blob-2
 
@@ -284,13 +287,13 @@
 '
 
 test_expect_success 'A: verify diff' '
-	copy=$(git rev-parse --verify master:file2) &&
+	copy=$(git rev-parse --verify main:file2) &&
 	cat >expect <<-EOF &&
 	:000000 100755 $ZERO_OID $copy A	copy-of-file2
 	EOF
-	git diff-tree -M -r master verify--import-marks >actual &&
+	git diff-tree -M -r main verify--import-marks >actual &&
 	compare_diff_raw expect actual &&
-	test $(git rev-parse --verify master:file2) \
+	test $(git rev-parse --verify main:file2) \
 	    = $(git rev-parse --verify verify--import-marks:copy-of-file2)
 '
 
@@ -364,7 +367,7 @@
 	corrupt
 	COMMIT
 
-	from refs/heads/master
+	from refs/heads/main
 	M 755 $(echo $ZERO_OID | sed -e "s/0$/1/") zero1
 
 	INPUT_END
@@ -381,7 +384,7 @@
 	tag base
 	COMMIT
 
-	from refs/heads/master
+	from refs/heads/main
 
 	INPUT_END
 
@@ -390,7 +393,7 @@
 		git prune" &&
 	git fast-import <input &&
 	test -f .git/TEMP_TAG &&
-	test $(git rev-parse master) = $(git rev-parse TEMP_TAG^)
+	test $(git rev-parse main) = $(git rev-parse TEMP_TAG^)
 '
 
 test_expect_success 'B: accept empty committer' '
@@ -528,8 +531,8 @@
 
 test_expect_success 'C: incremental import create pack from stdin' '
 	newf=$(echo hi newf | git hash-object -w --stdin) &&
-	oldf=$(git rev-parse --verify master:file2) &&
-	thrf=$(git rev-parse --verify master:file3) &&
+	oldf=$(git rev-parse --verify main:file2) &&
+	thrf=$(git rev-parse --verify main:file3) &&
 	test_tick &&
 	cat >input <<-INPUT_END &&
 	commit refs/heads/branch
@@ -538,7 +541,7 @@
 	second
 	COMMIT
 
-	from refs/heads/master
+	from refs/heads/main
 	M 644 $oldf file2/oldf
 	M 755 $newf file2/newf
 	D file3
@@ -560,7 +563,7 @@
 
 test_expect_success 'C: verify commit' '
 	cat >expect <<-EOF &&
-	parent $(git rev-parse --verify master^0)
+	parent $(git rev-parse --verify main^0)
 	author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
@@ -578,7 +581,7 @@
 	:100644 100644 $oldf $oldf R100	file2	file2/oldf
 	:100644 000000 $thrf $zero D	file3
 	EOF
-	git diff-tree -M -r master branch >actual &&
+	git diff-tree -M -r main branch >actual &&
 	compare_diff_raw expect actual
 '
 
@@ -1629,7 +1632,10 @@
 	INPUT_END
 
 	git fast-import <input &&
-	test 8 = $(find .git/objects/pack -type f | grep -v multi-pack-index | wc -l) &&
+	ls -la .git/objects/pack/pack-*.pack >packlist &&
+	ls -la .git/objects/pack/pack-*.pack >idxlist &&
+	test_line_count = 4 idxlist &&
+	test_line_count = 4 packlist &&
 	test $(git rev-parse refs/tags/O3-2nd) = $(git rev-parse O3^) &&
 	git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
 	test_cmp expect actual
@@ -1698,7 +1704,7 @@
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	data 8
 	initial
-	from refs/heads/master
+	from refs/heads/main
 	M 100644 :3 .gitmodules
 	M 160000 :2 sub
 
@@ -1733,8 +1739,8 @@
 	(
 		cd sub &&
 		git init &&
-		git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
-		git checkout master
+		git fetch --update-head-ok .. refs/heads/sub:refs/heads/main &&
+		git checkout main
 	) &&
 	git submodule init &&
 	git submodule update
@@ -1758,7 +1764,7 @@
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	data 8
 	initial
-	from refs/heads/master
+	from refs/heads/main
 	M 100644 :1 .gitmodules
 	M 160000 $SUBPREV sub
 
@@ -3429,8 +3435,8 @@
 data 4
 foo
 
-reset refs/heads/master
-commit refs/heads/master
+reset refs/heads/main
+commit refs/heads/main
 mark :2
 author Full Name <user@company.tld> 1000000000 +0100
 committer Full Name <user@company.tld> 1000000000 +0100
@@ -3444,7 +3450,7 @@
 foo
 bar
 
-commit refs/heads/master
+commit refs/heads/main
 mark :4
 author Full Name <user@company.tld> 1000000001 +0100
 committer Full Name <user@company.tld> 1000000001 +0100
@@ -3461,8 +3467,8 @@
 data 4
 foo
 
-reset refs/heads/master
-commit refs/heads/master
+reset refs/heads/main
+commit refs/heads/main
 mark :2
 author Full Name <user@company.tld> 2000000000 +0100
 committer Full Name <user@company.tld> 2000000000 +0100
@@ -3477,7 +3483,7 @@
 	path = sub1
 	url = https://void.example.com/main.git
 
-commit refs/heads/master
+commit refs/heads/main
 mark :4
 author Full Name <user@company.tld> 2000000001 +0100
 committer Full Name <user@company.tld> 2000000001 +0100
@@ -3493,7 +3499,7 @@
 foo
 bar
 
-commit refs/heads/master
+commit refs/heads/main
 mark :6
 author Full Name <user@company.tld> 2000000002 +0100
 committer Full Name <user@company.tld> 2000000002 +0100
@@ -3511,8 +3517,8 @@
 
 test_expect_success 'Y: setup' '
 	test_oid_cache <<-EOF
-	Ymaster sha1:9afed2f9161ddf416c0a1863b8b0725b00070504
-	Ymaster sha256:c0a1010da1df187b2e287654793df01b464bd6f8e3f17fc1481a7dadf84caee3
+	Ymain sha1:9afed2f9161ddf416c0a1863b8b0725b00070504
+	Ymain sha256:c0a1010da1df187b2e287654793df01b464bd6f8e3f17fc1481a7dadf84caee3
 	EOF
 '
 
@@ -3524,7 +3530,7 @@
 		git -C sub2 fast-import --export-marks=../sub2-marks <../Y-sub-input &&
 		git fast-import --rewrite-submodules-from=sub:../Y-marks \
 			--rewrite-submodules-to=sub:sub2-marks <../Y-main-input &&
-		test "$(git rev-parse master)" = "$(test_oid Ymaster)"
+		test "$(git rev-parse main)" = "$(test_oid Ymain)"
 	)
 '
 
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
index 14c1baa..1ae4d7c 100755
--- a/t/t9301-fast-import-notes.sh
+++ b/t/t9301-fast-import-notes.sh
@@ -4,12 +4,15 @@
 #
 
 test_description='test git fast-import of notes objects'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 
 test_tick
 cat >input <<INPUT_END
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 first commit
@@ -30,7 +33,7 @@
 file baz/xyzzy in first commit
 EOF
 
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 second commit
@@ -46,7 +49,7 @@
 file baz/xyzzy in second commit
 EOF
 
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 third commit
@@ -57,7 +60,7 @@
 file foo in third commit
 EOF
 
-commit refs/heads/master
+commit refs/heads/main
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
 fourth commit
@@ -70,13 +73,13 @@
 
 INPUT_END
 
-test_expect_success 'set up master branch' '
+test_expect_success 'set up main branch' '
 
 	git fast-import <input &&
-	git whatchanged master
+	git whatchanged main
 '
 
-commit4=$(git rev-parse refs/heads/master)
+commit4=$(git rev-parse refs/heads/main)
 commit3=$(git rev-parse "$commit4^")
 commit2=$(git rev-parse "$commit4~2")
 commit1=$(git rev-parse "$commit4~3")
diff --git a/t/t9302-fast-import-unpack-limit.sh b/t/t9302-fast-import-unpack-limit.sh
index bb1c39c..f519e4f 100755
--- a/t/t9302-fast-import-unpack-limit.sh
+++ b/t/t9302-fast-import-unpack-limit.sh
@@ -5,7 +5,7 @@
 test_expect_success 'create loose objects on import' '
 	test_tick &&
 	cat >input <<-INPUT_END &&
-	commit refs/heads/master
+	commit refs/heads/main
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	data <<COMMIT
 	initial
@@ -23,12 +23,12 @@
 test_expect_success 'bigger packs are preserved' '
 	test_tick &&
 	cat >input <<-INPUT_END &&
-	commit refs/heads/master
+	commit refs/heads/main
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 	data <<COMMIT
 	incremental should create a pack
 	COMMIT
-	from refs/heads/master^0
+	from refs/heads/main^0
 
 	commit refs/heads/branch
 	committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -48,7 +48,7 @@
 test_expect_success 'lookups after checkpoint works' '
 	hello_id=$(echo hello | git hash-object --stdin -t blob) &&
 	id="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
-	before=$(git rev-parse refs/heads/master^0) &&
+	before=$(git rev-parse refs/heads/main^0) &&
 	(
 		cat <<-INPUT_END &&
 		blob
@@ -56,13 +56,13 @@
 		data 6
 		hello
 
-		commit refs/heads/master
+		commit refs/heads/main
 		mark :2
 		committer $id
 		data <<COMMIT
 		checkpoint after this
 		COMMIT
-		from refs/heads/master^0
+		from refs/heads/main^0
 		M 100644 :1 hello
 
 		# pre-checkpoint
@@ -86,10 +86,10 @@
 				n=$(($n + 1))
 			fi &&
 			sleep 1 &&
-			from=$(git rev-parse refs/heads/master^0)
+			from=$(git rev-parse refs/heads/main^0)
 		done &&
 		cat <<-INPUT_END &&
-		commit refs/heads/master
+		commit refs/heads/main
 		committer $id
 		data <<COMMIT
 		make sure from "unpacked sha1 reference" works, too
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 1372842..409b48e 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -4,6 +4,9 @@
 #
 
 test_description='git fast-export'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -31,13 +34,13 @@
 	git commit -m sitzt file2 &&
 	test_tick &&
 	git tag -a -m valentin muss &&
-	git merge -s ours master
+	git merge -s ours main
 
 '
 
 test_expect_success 'fast-export | fast-import' '
 
-	MASTER=$(git rev-parse --verify master) &&
+	MAIN=$(git rev-parse --verify main) &&
 	REIN=$(git rev-parse --verify rein) &&
 	WER=$(git rev-parse --verify wer) &&
 	MUSS=$(git rev-parse --verify muss) &&
@@ -46,7 +49,7 @@
 	git fast-export --all >actual &&
 	(cd new &&
 	 git fast-import &&
-	 test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+	 test $MAIN = $(git rev-parse --verify refs/heads/main) &&
 	 test $REIN = $(git rev-parse --verify refs/tags/rein) &&
 	 test $WER = $(git rev-parse --verify refs/heads/wer) &&
 	 test $MUSS = $(git rev-parse --verify refs/tags/muss)) <actual
@@ -80,35 +83,35 @@
 	test_cmp expected actual
 '
 
-test_expect_success 'fast-export master~2..master' '
+test_expect_success 'fast-export main~2..main' '
 
-	git fast-export master~2..master >actual &&
-	sed "s/master/partial/" actual |
+	git fast-export main~2..main >actual &&
+	sed "s/main/partial/" actual |
 		(cd new &&
 		 git fast-import &&
-		 test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
-		 git diff --exit-code master partial &&
-		 git diff --exit-code master^ partial^ &&
+		 test $MAIN != $(git rev-parse --verify refs/heads/partial) &&
+		 git diff --exit-code main partial &&
+		 git diff --exit-code main^ partial^ &&
 		 test_must_fail git rev-parse partial~2)
 
 '
 
-test_expect_success 'fast-export --reference-excluded-parents master~2..master' '
+test_expect_success 'fast-export --reference-excluded-parents main~2..main' '
 
-	git fast-export --reference-excluded-parents master~2..master >actual &&
-	grep commit.refs/heads/master actual >commit-count &&
+	git fast-export --reference-excluded-parents main~2..main >actual &&
+	grep commit.refs/heads/main actual >commit-count &&
 	test_line_count = 2 commit-count &&
-	sed "s/master/rewrite/" actual |
+	sed "s/main/rewrite/" actual |
 		(cd new &&
 		 git fast-import &&
-		 test $MASTER = $(git rev-parse --verify refs/heads/rewrite))
+		 test $MAIN = $(git rev-parse --verify refs/heads/rewrite))
 '
 
 test_expect_success 'fast-export --show-original-ids' '
 
-	git fast-export --show-original-ids master >output &&
+	git fast-export --show-original-ids main >output &&
 	grep ^original-oid output| sed -e s/^original-oid.// | sort >actual &&
-	git rev-list --objects master muss >objects-and-names &&
+	git rev-list --objects main muss >objects-and-names &&
 	awk "{print \$1}" objects-and-names | sort >commits-trees-blobs &&
 	comm -23 actual commits-trees-blobs >unfound &&
 	test_must_be_empty unfound
@@ -116,8 +119,8 @@
 
 test_expect_success 'fast-export --show-original-ids | git fast-import' '
 
-	git fast-export --show-original-ids master muss | git fast-import --quiet &&
-	test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+	git fast-export --show-original-ids main muss | git fast-import --quiet &&
+	test $MAIN = $(git rev-parse --verify refs/heads/main) &&
 	test $MUSS = $(git rev-parse --verify refs/tags/muss)
 '
 
@@ -200,7 +203,7 @@
 
 test_expect_success 'import/export-marks' '
 
-	git checkout -b marks master &&
+	git checkout -b marks main &&
 	git fast-export --export-marks=tmp-marks HEAD &&
 	test -s tmp-marks &&
 	test_line_count = 3 tmp-marks &&
@@ -265,7 +268,7 @@
 
 test_expect_success 'setup submodule' '
 
-	git checkout -f master &&
+	git checkout -f main &&
 	mkdir sub &&
 	(
 		cd sub &&
@@ -290,17 +293,17 @@
 
 test_expect_success 'submodule fast-export | fast-import' '
 
-	SUBENT1=$(git ls-tree master^ sub) &&
-	SUBENT2=$(git ls-tree master sub) &&
+	SUBENT1=$(git ls-tree main^ sub) &&
+	SUBENT2=$(git ls-tree main sub) &&
 	rm -rf new &&
 	mkdir new &&
 	git --git-dir=new/.git init &&
 	git fast-export --signed-tags=strip --all >actual &&
 	(cd new &&
 	 git fast-import &&
-	 test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
-	 test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
-	 git checkout master &&
+	 test "$SUBENT1" = "$(git ls-tree refs/heads/main^ sub)" &&
+	 test "$SUBENT2" = "$(git ls-tree refs/heads/main sub)" &&
+	 git checkout main &&
 	 git submodule init &&
 	 git submodule update &&
 	 cmp sub/file ../sub/file) <actual
@@ -352,7 +355,7 @@
 
 '
 
-test_expect_success 'fast-export | fast-import when master is tagged' '
+test_expect_success 'fast-export | fast-import when main is tagged' '
 
 	git tag -m msg last &&
 	git fast-export -C -C --signed-tags=strip --all > output &&
@@ -467,8 +470,8 @@
 data 3
 hi
 
-reset refs/heads/master
-commit refs/heads/master
+reset refs/heads/main
+commit refs/heads/main
 mark :3
 author A U Thor <author@example.com> 1112912713 -0700
 committer C O Mitter <committer@example.com> 1112912713 -0700
@@ -482,7 +485,7 @@
 test_expect_failure 'no exact-ref revisions included' '
 	(
 		cd limit-by-paths &&
-		git fast-export master~2..master~1 > output &&
+		git fast-export main~2..main~1 > output &&
 		test_cmp expected output
 	)
 '
@@ -524,7 +527,7 @@
 '
 
 test_expect_success 'set-up a few more tags for tag export tests' '
-	git checkout -f master &&
+	git checkout -f main &&
 	HEAD_TREE=$(git show -s --pretty=raw HEAD | grep tree | sed "s/tree //") &&
 	git tag    tree_tag        -m "tagging a tree" $HEAD_TREE &&
 	git tag -a tree_tag-obj    -m "tagging a tree" $HEAD_TREE &&
@@ -549,7 +552,7 @@
 test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
 
 test_expect_success 'handling tags of blobs' '
-	git tag -a -m "Tag of a blob" blobtag $(git rev-parse master:file) &&
+	git tag -a -m "Tag of a blob" blobtag $(git rev-parse main:file) &&
 	git fast-export blobtag >actual &&
 	cat >expect <<-EOF &&
 	blob
@@ -592,10 +595,10 @@
 	) &&
 	(
 		cd dirtosymlink &&
-		git fast-export master -- foo |
+		git fast-export main -- foo |
 		(cd ../result && git fast-import --quiet)
 	) &&
-	(cd result && git show master:foo)
+	(cd result && git show main:foo)
 '
 
 test_expect_success 'fast-export quotes pathnames' '
@@ -643,7 +646,7 @@
 data 5
 bump
 
-commit refs/heads/master
+commit refs/heads/main
 mark :14
 author A U Thor <author@example.com> 1112912773 -0700
 committer C O Mitter <committer@example.com> 1112912773 -0700
@@ -657,18 +660,18 @@
 test_expect_success 'avoid uninteresting refs' '
 	> tmp-marks &&
 	git fast-export --import-marks=tmp-marks \
-		--export-marks=tmp-marks master > /dev/null &&
+		--export-marks=tmp-marks main > /dev/null &&
 	git tag v1.0 &&
 	git branch uninteresting &&
 	echo bump > file &&
 	git commit -a -m bump &&
 	git fast-export --import-marks=tmp-marks \
-		--export-marks=tmp-marks ^uninteresting ^v1.0 master > actual &&
+		--export-marks=tmp-marks ^uninteresting ^v1.0 main > actual &&
 	test_cmp expected actual
 '
 
 cat > expected << EOF
-reset refs/heads/master
+reset refs/heads/main
 from :14
 
 EOF
@@ -676,14 +679,14 @@
 test_expect_success 'refs are updated even if no commits need to be exported' '
 	> tmp-marks &&
 	git fast-export --import-marks=tmp-marks \
-		--export-marks=tmp-marks master > /dev/null &&
+		--export-marks=tmp-marks main > /dev/null &&
 	git fast-export --import-marks=tmp-marks \
-		--export-marks=tmp-marks master > actual &&
+		--export-marks=tmp-marks main > actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'use refspec' '
-	git fast-export --refspec refs/heads/master:refs/heads/foobar master >actual2 &&
+	git fast-export --refspec refs/heads/main:refs/heads/foobar main >actual2 &&
 	grep "^commit " actual2 | sort | uniq >actual &&
 	echo "commit refs/heads/foobar" > expected &&
 	test_cmp expected actual
@@ -736,13 +739,13 @@
 		test_commit initial &&
 		git checkout -b topic &&
 		test_commit on-topic &&
-		git checkout master &&
-		test_commit on-master &&
+		git checkout main &&
+		test_commit on-main &&
 		test_tick &&
 		git merge --no-ff -m Yeah topic &&
 
 		echo ":1 $(git rev-parse HEAD^^)" >marks &&
-		git fast-export --import-marks=marks master >out &&
+		git fast-export --import-marks=marks main >out &&
 		grep Yeah out
 	)
 '
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
index 5ac2c3b..1c6e6fc 100755
--- a/t/t9351-fast-export-anonymize.sh
+++ b/t/t9351-fast-export-anonymize.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='basic tests for fast-export --anonymize'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 test_expect_success 'setup simple repo' '
@@ -51,7 +54,7 @@
 '
 
 test_expect_success 'stream omits other refnames' '
-	! grep master stream &&
+	! grep main stream &&
 	! grep mytag stream
 '
 
@@ -85,7 +88,7 @@
 	shape () {
 		git log --format="%m %ct" --left-right --boundary "$@"
 	} &&
-	(cd .. && shape master...other) >expect &&
+	(cd .. && shape main...other) >expect &&
 	shape $main_branch...$other_branch >actual &&
 	test_cmp expect actual
 '
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 4a46f31..2d29d48 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -8,6 +8,9 @@
 tests read access to a git repository with the
 cvs CLI client via git-cvsserver server'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 if ! test_have_prereq PERL; then
@@ -45,7 +48,7 @@
   touch secondrootfile &&
   git add secondrootfile &&
   git commit -m "second root") &&
-  git fetch secondroot master &&
+  git fetch secondroot main &&
   git merge --allow-unrelated-histories FETCH_HEAD &&
   git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
   GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
@@ -57,7 +60,7 @@
 # note that cvs doesn't accept absolute pathnames
 # as argument to co -d
 test_expect_success 'basic checkout' \
-  'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
+  'GIT_CONFIG="$git_config" cvs -Q co -d cvswork main &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/" &&
    test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
 
@@ -226,7 +229,7 @@
 
 test_expect_success 'gitcvs.enabled = false' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
-   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
    then
      echo unexpected cvs success
      false
@@ -240,14 +243,14 @@
 test_expect_success 'gitcvs.ext.enabled = true' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
    test_cmp cvswork cvswork2'
 
 rm -fr cvswork2
 test_expect_success 'gitcvs.ext.enabled = false' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
    GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
-   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+   if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1
    then
      echo unexpected cvs success
      false
@@ -261,21 +264,21 @@
 test_expect_success 'gitcvs.dbname' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
    test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
+   test -f "$SERVERDIR/gitcvs.ext.main.sqlite" &&
+   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs.ext.main.sqlite"'
 
 rm -fr cvswork2
 test_expect_success 'gitcvs.ext.dbname' \
   'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
    GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
    GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
-   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+   GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
    test_cmp cvswork cvswork2 &&
-   test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
-   test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
-   cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
+   test -f "$SERVERDIR/gitcvs1.ext.main.sqlite" &&
+   test ! -f "$SERVERDIR/gitcvs2.ext.main.sqlite" &&
+   cmp "$SERVERDIR/gitcvs.main.sqlite" "$SERVERDIR/gitcvs1.ext.main.sqlite"'
 
 
 #------------
@@ -457,7 +460,7 @@
 test_expect_success 'cvs update (module list supports packed refs)' '
     GIT_DIR="$SERVERDIR" git pack-refs --all &&
     GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
-    grep "cvs update: New directory \`master'\''" < out
+    grep "cvs update: New directory \`main'\''" < out
 '
 
 #------------
@@ -499,8 +502,8 @@
 cd "$WORKDIR"
 test_expect_success 'cvs co -c (shows module database)' '
     GIT_CONFIG="$git_config" cvs co -c > out &&
-    grep "^master[	 ][ 	]*master$" <out &&
-    ! grep -v "^master[	 ][ 	]*master$" <out
+    grep "^main[	 ][ 	]*main$" <out &&
+    ! grep -v "^main[	 ][ 	]*main$" <out
 '
 
 #------------
@@ -526,7 +529,7 @@
 
 sed -e 's/^x//' -e 's/SP$/ /' > "$WORKDIR/expect" <<EOF
 x
-xRCS file: $WORKDIR/gitcvs.git/master/merge,v
+xRCS file: $WORKDIR/gitcvs.git/main/merge,v
 xWorking file: merge
 xhead: 1.4
 xbranch:
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
index c7a0dd8..a34805a 100755
--- a/t/t9401-git-cvsserver-crlf.sh
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -9,6 +9,9 @@
 tests -kb mode for binary files when accessing a git
 repository using cvs CLI client via git-cvsserver server'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 marked_as () {
@@ -105,14 +108,14 @@
 '
 
 test_expect_success 'cvs co (default crlf)' '
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x""
 '
 
 rm -rf cvswork
 test_expect_success 'cvs co (allbinary)' '
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true &&
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     marked_as cvswork textfile.c -kb &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes -kb &&
@@ -125,7 +128,7 @@
 rm -rf cvswork cvs.log
 test_expect_success 'cvs co (use attributes/allbinary)' '
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true &&
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes -kb &&
@@ -138,7 +141,7 @@
 rm -rf cvswork
 test_expect_success 'cvs co (use attributes)' '
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false &&
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -194,7 +197,7 @@
 rm -rf cvswork
 test_expect_success 'cvs co (use attributes/guess)' '
     GIT_DIR="$SERVERDIR" git config gitcvs.allbinary guess &&
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -224,7 +227,7 @@
 rm -rf cvswork
 test_expect_success 'cvs co (guess)' '
     GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr false &&
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork main >cvs.log 2>&1 &&
     marked_as cvswork textfile.c "" &&
     marked_as cvswork binfile.bin -kb &&
     marked_as cvswork .gitattributes "" &&
@@ -239,7 +242,7 @@
 '
 
 test_expect_success 'cvs co another copy (guess)' '
-    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+    GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 main >cvs.log 2>&1 &&
     marked_as cvswork2 textfile.c "" &&
     marked_as cvswork2 binfile.bin -kb &&
     marked_as cvswork2 .gitattributes "" &&
@@ -335,7 +338,7 @@
 
 echo "starting update/merge" >> "${WORKDIR}/marked.log"
 test_expect_success 'update/merge full other copy (guess)' '
-    git pull gitcvs.git master &&
+    git pull gitcvs.git main &&
     sed "s/3/replaced_3/" < multilineTxt.c > ml.temp &&
     mv ml.temp multilineTxt.c &&
     git add multilineTxt.c &&
diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh
index 6436c91..2ee41f9 100755
--- a/t/t9402-git-cvsserver-refs.sh
+++ b/t/t9402-git-cvsserver-refs.sh
@@ -5,6 +5,9 @@
 tests ability for git-cvsserver to switch between and compare
 tags, branches and other git refspecs'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./test-lib.sh
 
 #########
@@ -115,7 +118,7 @@
 
 rm -rf cvswork
 test_expect_success 'cvs co v1' '
-	cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 &&
+	cvs -f -Q co -r v1 -d cvswork main >cvs.log 2>&1 &&
 	check_start_tree cvswork &&
 	check_file cvswork textfile.c v1 &&
 	check_file cvswork t2 v1 &&
@@ -128,7 +131,7 @@
 
 rm -rf cvswork
 test_expect_success 'cvs co b1' '
-	cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 &&
+	cvs -f co -r b1 -d cvswork main >cvs.log 2>&1 &&
 	check_start_tree cvswork &&
 	check_file cvswork textfile.c v1 &&
 	check_file cvswork t2 v1 &&
@@ -140,7 +143,7 @@
 '
 
 test_expect_success 'cvs co b1 [cvswork3]' '
-	cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 &&
+	cvs -f co -r b1 -d cvswork3 main >cvs.log 2>&1 &&
 	check_start_tree cvswork3 &&
 	check_file cvswork3 textfile.c v1 &&
 	check_file cvswork3 t2 v1 &&
@@ -265,7 +268,7 @@
 '
 
 test_expect_success 'cvs co b2 [into cvswork2]' '
-	cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 &&
+	cvs -f co -r b2 -d cvswork2 main >cvs.log 2>&1 &&
 	check_start_tree cvswork &&
 	check_file cvswork textfile.c v1 &&
 	check_file cvswork t2 v1 &&
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index b484e3e..0333065 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -10,7 +10,10 @@
 or warnings to log.'
 
 
-. ./gitweb-lib.sh
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./lib-gitweb.sh
 
 # ----------------------------------------------------------------------
 # no commits (empty, just initialized repository)
@@ -327,7 +330,7 @@
 	 echo "Branch" >>b &&
 	 git add b &&
 	 git commit -a -m "On branch" &&
-	 git checkout master &&
+	 git checkout main &&
 	 git merge b &&
 	 git tag merge_commit'
 
@@ -364,7 +367,7 @@
 	 echo "Changed and have mode changed" >07-change-mode-change &&
 	 test_chmod +x 07-change-mode-change &&
 	 git commit -a -m "Large commit" &&
-	 git checkout master'
+	 git checkout main'
 
 test_expect_success \
 	'commit(1): large commit' \
@@ -402,7 +405,7 @@
 
 test_expect_success \
 	'tags: list of different types of tags' \
-	'git checkout master &&
+	'git checkout main &&
 	 git tag -a -m "Tag commit object" tag-commit HEAD &&
 	 git tag -a -m "" tag-commit-nomessage HEAD &&
 	 git tag -a -m "Tag tag object" tag-tag tag-commit &&
@@ -443,7 +446,7 @@
 
 test_expect_success \
 	'logs: history (implicit HEAD, deleted file)' \
-	'git checkout master &&
+	'git checkout main &&
 	 echo "to be deleted" >deleted_file &&
 	 git add deleted_file &&
 	 git commit -m "Add file to be deleted" &&
@@ -463,11 +466,11 @@
 
 test_expect_success \
 	'path_info: project/branch:file' \
-	'gitweb_run "" "/.git/master:file"'
+	'gitweb_run "" "/.git/main:file"'
 
 test_expect_success \
 	'path_info: project/branch:dir/' \
-	'gitweb_run "" "/.git/master:foo/"'
+	'gitweb_run "" "/.git/main:foo/"'
 
 test_expect_success \
 	'path_info: project/branch (non-existent)' \
@@ -479,16 +482,16 @@
 
 test_expect_success \
 	'path_info: project/branch:file (non-existent)' \
-	'gitweb_run "" "/.git/master:non-existent"'
+	'gitweb_run "" "/.git/main:non-existent"'
 
 test_expect_success \
 	'path_info: project/branch:dir/ (non-existent)' \
-	'gitweb_run "" "/.git/master:non-existent/"'
+	'gitweb_run "" "/.git/main:non-existent/"'
 
 
 test_expect_success \
 	'path_info: project/branch:/file' \
-	'gitweb_run "" "/.git/master:/file"'
+	'gitweb_run "" "/.git/main:/file"'
 
 test_expect_success \
 	'path_info: project/:/file (implicit HEAD)' \
@@ -801,7 +804,7 @@
 		git checkout orphan_branch ||
 		git checkout --orphan orphan_branch
 	 } &&
-	 test_when_finished "git checkout master" &&
+	 test_when_finished "git checkout main" &&
 	 gitweb_run "p=.git;a=summary"'
 
 test_done
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 2a0ffed..32814e7 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -10,7 +10,10 @@
 code and message.'
 
 
-. ./gitweb-lib.sh
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./lib-gitweb.sh
 
 #
 # Gitweb only provides the functionality tested by the 'modification times'
@@ -85,7 +88,7 @@
 # snapshot hash ids
 
 test_expect_success 'snapshots: good tree-ish id' '
-	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" &&
 	grep "Status: 200 OK" gitweb.output
 '
 test_debug 'cat gitweb.headers'
@@ -123,7 +126,7 @@
 # modification times (Last-Modified and If-Modified-Since)
 
 test_expect_success DATE_PARSER 'modification: feed last-modified' '
-	gitweb_run "p=.git;a=atom;h=master" &&
+	gitweb_run "p=.git;a=atom;h=main" &&
 	grep "Status: 200 OK" gitweb.headers &&
 	grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
 '
@@ -133,7 +136,7 @@
 	HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
 	export HTTP_IF_MODIFIED_SINCE &&
 	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
-	gitweb_run "p=.git;a=atom;h=master" &&
+	gitweb_run "p=.git;a=atom;h=main" &&
 	grep "Status: 200 OK" gitweb.headers
 '
 test_debug 'cat gitweb.headers'
@@ -142,13 +145,13 @@
 	HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
 	export HTTP_IF_MODIFIED_SINCE &&
 	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
-	gitweb_run "p=.git;a=atom;h=master" &&
+	gitweb_run "p=.git;a=atom;h=main" &&
 	grep "Status: 304 Not Modified" gitweb.headers
 '
 test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: snapshot last-modified' '
-	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" &&
 	grep "Status: 200 OK" gitweb.headers &&
 	grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
 '
@@ -158,7 +161,7 @@
 	HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
 	export HTTP_IF_MODIFIED_SINCE &&
 	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
-	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" &&
 	grep "Status: 200 OK" gitweb.headers
 '
 test_debug 'cat gitweb.headers'
@@ -167,7 +170,7 @@
 	HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
 	export HTTP_IF_MODIFIED_SINCE &&
 	test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
-	gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+	gitweb_run "p=.git;a=snapshot;h=main;sf=tgz" &&
 	grep "Status: 304 Not Modified" gitweb.headers
 '
 test_debug 'cat gitweb.headers'
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
index e38cbc9..3167473 100755
--- a/t/t9502-gitweb-standalone-parse-output.sh
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -10,7 +10,10 @@
 in the HTTP header or the actual script output.'
 
 
-. ./gitweb-lib.sh
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./lib-gitweb.sh
 
 # ----------------------------------------------------------------------
 # snapshot file name and prefix
@@ -79,10 +82,10 @@
 '
 test_debug 'cat gitweb.headers && cat file_list'
 
-test_expect_success 'snapshot: short branch name (master)' '
-	gitweb_run "p=.git;a=snapshot;h=master;sf=tar" &&
-	ID=$(git rev-parse --verify --short=7 master) &&
-	check_snapshot ".git-master-$ID"
+test_expect_success 'snapshot: short branch name (main)' '
+	gitweb_run "p=.git;a=snapshot;h=main;sf=tar" &&
+	ID=$(git rev-parse --verify --short=7 main) &&
+	check_snapshot ".git-main-$ID"
 '
 test_debug 'cat gitweb.headers && cat file_list'
 
@@ -93,10 +96,10 @@
 '
 test_debug 'cat gitweb.headers && cat file_list'
 
-test_expect_success 'snapshot: full branch name (refs/heads/master)' '
-	gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" &&
-	ID=$(git rev-parse --verify --short=7 master) &&
-	check_snapshot ".git-master-$ID"
+test_expect_success 'snapshot: full branch name (refs/heads/main)' '
+	gitweb_run "p=.git;a=snapshot;h=refs/heads/main;sf=tar" &&
+	ID=$(git rev-parse --verify --short=7 main) &&
+	check_snapshot ".git-main-$ID"
 '
 test_debug 'cat gitweb.headers && cat file_list'
 
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 251fdd6..5680849 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_description='git cvsimport basic tests'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-cvs.sh
 
 if ! test_have_prereq NOT_ROOT; then
@@ -159,6 +162,6 @@
 
 '
 
-test_expect_success PERL 'test entire HEAD' 'test_cmp_branch_tree master'
+test_expect_success PERL 'test entire HEAD' 'test_cmp_branch_tree main'
 
 test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
index 827d39f..116cddb 100755
--- a/t/t9601-cvsimport-vendor-branch.sh
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -32,6 +32,9 @@
 #       tag has been removed.
 
 test_description='git cvsimport handling of vendor branches'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-cvs.sh
 
 setup_cvs_test_repository t9601
@@ -42,43 +45,43 @@
 
 '
 
-test_expect_success PERL 'check HEAD out of cvs repository' 'test_cvs_co master'
+test_expect_success PERL 'check HEAD out of cvs repository' 'test_cvs_co main'
 
-test_expect_success PERL 'check master out of git repository' 'test_git_co master'
+test_expect_success PERL 'check main out of git repository' 'test_git_co main'
 
 test_expect_success PERL 'check a file that was imported once' '
 
-	test_cmp_branch_file master imported-once.txt
+	test_cmp_branch_file main imported-once.txt
 
 '
 
 test_expect_failure PERL 'check a file that was imported twice' '
 
-	test_cmp_branch_file master imported-twice.txt
+	test_cmp_branch_file main imported-twice.txt
 
 '
 
 test_expect_success PERL 'check a file that was imported then modified on HEAD' '
 
-	test_cmp_branch_file master imported-modified.txt
+	test_cmp_branch_file main imported-modified.txt
 
 '
 
 test_expect_success PERL 'check a file that was imported, modified, then imported again' '
 
-	test_cmp_branch_file master imported-modified-imported.txt
+	test_cmp_branch_file main imported-modified-imported.txt
 
 '
 
 test_expect_success PERL 'check a file that was added to HEAD then imported' '
 
-	test_cmp_branch_file master added-imported.txt
+	test_cmp_branch_file main added-imported.txt
 
 '
 
 test_expect_success PERL 'a vendor branch whose tag has been removed' '
 
-	test_cmp_branch_file master imported-anonymously.txt
+	test_cmp_branch_file main imported-anonymously.txt
 
 '
 
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
index e1db323..e5266c9 100755
--- a/t/t9602-cvsimport-branches-tags.sh
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -4,6 +4,9 @@
 # t9602/README.
 
 test_description='git cvsimport handling of branches and tags'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-cvs.sh
 
 setup_cvs_test_repository t9602
@@ -14,9 +17,9 @@
 
 '
 
-test_expect_success PERL 'test branch master' '
+test_expect_success PERL 'test branch main' '
 
-	test_cmp_branch_tree master
+	test_cmp_branch_tree main
 
 '
 
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index e7a9161..0e9daa5 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -12,6 +12,9 @@
 # bug.
 
 test_description='git cvsimport testing for correct patchset estimation'
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-cvs.sh
 
 setup_cvs_test_repository t9603
@@ -20,16 +23,16 @@
 	git cvsimport -p"-x" -C module-git module &&
 	(
 		cd module-git &&
-		git log --pretty=format:%s > ../actual-master &&
+		git log --pretty=format:%s > ../actual-main &&
 		git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
-		echo "" >> ../actual-master &&
+		echo "" >> ../actual-main &&
 		echo "" >> ../actual-A
 	) &&
 	echo "Rev 4
 Rev 3
 Rev 2
-Rev 1" > expect-master &&
-	test_cmp expect-master actual-master &&
+Rev 1" > expect-main &&
+	test_cmp expect-main actual-main &&
 
 	echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
 Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index c98c1df..81bc8e8 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -2,6 +2,9 @@
 
 test_description='git p4 tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-p4.sh
 
 test_expect_success 'start p4d' '
@@ -226,7 +229,7 @@
 		git config --get --bool core.bare true &&
 		git rev-parse --verify refs/remotes/p4/master &&
 		git rev-parse --verify refs/remotes/p4/HEAD &&
-		git rev-parse --verify refs/heads/master &&
+		git rev-parse --verify refs/heads/main &&
 		git rev-parse --verify HEAD
 	)
 '
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index a3abd77..56e6469 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -2,6 +2,9 @@
 
 test_description='git p4 tests for p4 branches'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-p4.sh
 
 test_expect_success 'start p4d' '
@@ -67,7 +70,7 @@
 	(
 		cd "$git" &&
 		git log --oneline --graph --decorate --all &&
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 4 wc
 	)
 '
@@ -78,7 +81,7 @@
 	(
 		cd "$git" &&
 		git log --oneline --graph --decorate --all &&
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 2 wc
 	)
 '
@@ -89,7 +92,7 @@
 	(
 		cd "$git" &&
 		git log --oneline --graph --decorate --all &&
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 2 wc
 	)
 '
@@ -100,7 +103,7 @@
 	(
 		cd "$git" &&
 		git log --oneline --graph --decorate --all &&
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 8 wc
 	)
 '
@@ -114,7 +117,7 @@
 		git log --oneline --graph --decorate --all &&
 
 		# 4 main commits
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 4 wc &&
 
 		# 3 main, 1 integrate, 1 on branch2
@@ -137,7 +140,7 @@
 		git log --oneline --graph --decorate --all &&
 
 		# 4 main commits
-		git rev-list master -- >wc &&
+		git rev-list main -- >wc &&
 		test_line_count = 4 wc &&
 
 		# 3 main, 1 integrate, 1 on branch2
@@ -484,7 +487,7 @@
 	(
 		cd "$git" &&
 		git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
-		git checkout -b master p4/usecs/b1 &&
+		git checkout -b main p4/usecs/b1 &&
 		test_path_is_file b1-file1 &&
 		test_path_is_missing b2-file2 &&
 		test_path_is_missing b1 &&
@@ -537,7 +540,7 @@
 	(
 		cd "$git" &&
 		git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
-		git checkout -b master p4/usecs/b3 &&
+		git checkout -b main p4/usecs/b3 &&
 		test_path_is_file b1-file1 &&
 		test_path_is_file b3-file3_2 &&
 		test_path_is_missing b3-file3_1
diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh
index 4e794a0..c26d297 100755
--- a/t/t9806-git-p4-options.sh
+++ b/t/t9806-git-p4-options.sh
@@ -2,6 +2,9 @@
 
 test_description='git p4 options'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-p4.sh
 
 test_expect_success 'start p4d' '
@@ -27,14 +30,14 @@
 	test_must_fail git p4 clone --git-dir=xx //depot
 '
 
-test_expect_success 'clone --branch should checkout master' '
+test_expect_success 'clone --branch should checkout main' '
 	git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
 	test_when_finished cleanup_git &&
 	(
 		cd "$git" &&
 		git rev-parse refs/remotes/p4/sb >sb &&
-		git rev-parse refs/heads/master >master &&
-		test_cmp sb master &&
+		git rev-parse refs/heads/main >main &&
+		test_cmp sb main &&
 		git rev-parse HEAD >head &&
 		test_cmp sb head
 	)
@@ -147,22 +150,22 @@
 	test_must_fail git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot@all
 '
 
-# imports both master and p4/master in refs/heads
+# imports both main and p4/master in refs/heads
 # requires --import-local on sync to find p4 refs/heads
-# does not update master on sync, just p4/master
+# does not update main on sync, just p4/master
 test_expect_success 'clone/sync --import-local' '
 	git p4 clone --import-local --dest="$git" //depot@1,2 &&
 	test_when_finished cleanup_git &&
 	(
 		cd "$git" &&
-		git log --oneline refs/heads/master >lines &&
+		git log --oneline refs/heads/main >lines &&
 		test_line_count = 2 lines &&
 		git log --oneline refs/heads/p4/master >lines &&
 		test_line_count = 2 lines &&
 		test_must_fail git p4 sync &&
 
 		git p4 sync --import-local &&
-		git log --oneline refs/heads/master >lines &&
+		git log --oneline refs/heads/main >lines &&
 		test_line_count = 2 lines &&
 		git log --oneline refs/heads/p4/master >lines &&
 		test_line_count = 3 lines
@@ -174,7 +177,7 @@
 	test_when_finished cleanup_git &&
 	(
 		cd "$git" &&
-		git log --oneline refs/heads/master >lines &&
+		git log --oneline refs/heads/main >lines &&
 		test_line_count = 2 lines
 	)
 '
@@ -237,7 +240,7 @@
 			git init &&
 			git config git-p4.useClientSpec true &&
 			git p4 sync //depot/... &&
-			git checkout -b master p4/master &&
+			git checkout -b main p4/master &&
 			test_path_is_file bus/dir/f4 &&
 			test_path_is_missing file1
 		)
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index eaaae41..7d4109f 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -2,6 +2,9 @@
 
 test_description='git p4 submit'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-p4.sh
 
 test_expect_success 'start p4d' '
@@ -114,7 +117,7 @@
 		git config git-p4.skipSubmitEdit true &&
 		git config git-p4.allowSubmit "nobranch" &&
 		test_must_fail git p4 submit &&
-		git config git-p4.allowSubmit "nobranch,master" &&
+		git config git-p4.allowSubmit "nobranch,main" &&
 		git p4 submit
 	)
 '
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index c1446f2..5ac5383 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -2,6 +2,9 @@
 
 test_description='git p4 label tests'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-git-p4.sh
 
 test_expect_success 'start p4d' '
@@ -185,7 +188,7 @@
 		git add main/f12 &&
 		git commit -m "adding f12" &&
 		git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
-		git checkout master &&
+		git checkout main &&
 		git p4 submit --export-labels
 	) &&
 	(
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index a1c4f1f..04ce884 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -5,6 +5,9 @@
 
 test_description='test bash completion'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-bash.sh
 
 complete ()
@@ -2363,7 +2366,6 @@
 '
 
 test_expect_success 'sourcing the completion script clears cached merge strategies' '
-	GIT_TEST_GETTEXT_POISON=false &&
 	__git_compute_merge_strategies &&
 	verbose test -n "$__git_merge_strategies" &&
 	. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
@@ -2380,4 +2382,24 @@
 	verbose test -z "$__gitcomp_builtin_notes_edit"
 '
 
+test_expect_success '__git_complete' '
+	unset -f __git_wrap__git_main &&
+
+	__git_complete foo __git_main &&
+	__git_have_func __git_wrap__git_main &&
+	unset -f __git_wrap__git_main &&
+
+	__git_complete gf _git_fetch &&
+	__git_have_func __git_wrap_git_fetch &&
+
+	__git_complete foo git &&
+	__git_have_func __git_wrap__git_main &&
+	unset -f __git_wrap__git_main &&
+
+	__git_complete gd git_diff &&
+	__git_have_func __git_wrap_git_diff &&
+
+	test_must_fail __git_complete ga missing
+'
+
 test_done
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index ab5da2c..bbd513b 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -5,6 +5,9 @@
 
 test_description='test git-specific bash prompt functions'
 
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
 . ./lib-bash.sh
 
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
@@ -28,7 +31,7 @@
 	echo 3 >file &&
 	git commit -m "third b1" file &&
 	git tag -a -m msg2 t2 &&
-	git checkout -b b2 master &&
+	git checkout -b b2 main &&
 	echo 0 >file &&
 	git commit -m "second b2" file &&
 	echo 00 >file &&
@@ -37,20 +40,20 @@
 	git commit -m "yet another b2" file &&
 	mkdir ignored_dir &&
 	echo "ignored_dir/" >>.gitignore &&
-	git checkout master
+	git checkout main
 '
 
 test_expect_success 'prompt - branch name' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
-	printf " (master)" >expected &&
-	test_when_finished "git checkout master" &&
+	printf " (main)" >expected &&
+	test_when_finished "git checkout main" &&
 	test_config core.preferSymlinkRefs true &&
-	git checkout master &&
+	git checkout main &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
@@ -58,7 +61,7 @@
 test_expect_success 'prompt - unborn branch' '
 	printf " (unborn)" >expected &&
 	git checkout --orphan unborn &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
@@ -72,7 +75,7 @@
 with
 newline" &&
 	mkdir "$repo_with_newline" &&
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	git init "$repo_with_newline" &&
 	test_when_finished "rm -rf \"$repo_with_newline\"" &&
 	mkdir "$repo_with_newline"/subdir &&
@@ -87,7 +90,7 @@
 	printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
 	test_config core.abbrev 13 &&
 	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
@@ -95,7 +98,7 @@
 test_expect_success 'prompt - describe detached head - contains' '
 	printf " ((t2~1))" >expected &&
 	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=contains &&
 		__git_ps1 >"$actual"
@@ -106,7 +109,7 @@
 test_expect_success 'prompt - describe detached head - branch' '
 	printf " ((tags/t2~1))" >expected &&
 	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=branch &&
 		__git_ps1 >"$actual"
@@ -117,7 +120,7 @@
 test_expect_success 'prompt - describe detached head - describe' '
 	printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
 	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	(
 		GIT_PS1_DESCRIBE_STYLE=describe &&
 		__git_ps1 >"$actual"
@@ -128,7 +131,7 @@
 test_expect_success 'prompt - describe detached head - default' '
 	printf " ((t2))" >expected &&
 	git checkout --detach b1 &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	__git_ps1 >"$actual" &&
 	test_cmp expected "$actual"
 '
@@ -152,7 +155,7 @@
 '
 
 test_expect_success 'prompt - inside bare repository' '
-	printf " (BARE:master)" >expected &&
+	printf " (BARE:main)" >expected &&
 	git init --bare bare.git &&
 	test_when_finished "rm -rf bare.git" &&
 	(
@@ -172,7 +175,7 @@
 	test_when_finished "rm -f fake_editor.sh" &&
 	test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
 	git checkout b1 &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git rebase -i HEAD^ &&
 	test_when_finished "git rebase --abort" &&
 	__git_ps1 >"$actual" &&
@@ -182,7 +185,7 @@
 test_expect_success 'prompt - rebase merge' '
 	printf " (b2|REBASE 1/3)" >expected &&
 	git checkout b2 &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	test_must_fail git rebase --merge b1 b2 &&
 	test_when_finished "git rebase --abort" &&
 	__git_ps1 >"$actual" &&
@@ -192,7 +195,7 @@
 test_expect_success 'prompt - rebase am' '
 	printf " (b2|REBASE 1/3)" >expected &&
 	git checkout b2 &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	test_must_fail git rebase --apply b1 b2 &&
 	test_when_finished "git rebase --abort" &&
 	__git_ps1 >"$actual" &&
@@ -202,7 +205,7 @@
 test_expect_success 'prompt - merge' '
 	printf " (b1|MERGING)" >expected &&
 	git checkout b1 &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	test_must_fail git merge b2 &&
 	test_when_finished "git reset --hard" &&
 	__git_ps1 >"$actual" &&
@@ -210,7 +213,7 @@
 '
 
 test_expect_success 'prompt - cherry-pick' '
-	printf " (master|CHERRY-PICKING)" >expected &&
+	printf " (main|CHERRY-PICKING)" >expected &&
 	test_must_fail git cherry-pick b1 b1^ &&
 	test_when_finished "git cherry-pick --abort" &&
 	__git_ps1 >"$actual" &&
@@ -222,7 +225,7 @@
 '
 
 test_expect_success 'prompt - revert' '
-	printf " (master|REVERTING)" >expected &&
+	printf " (main|REVERTING)" >expected &&
 	test_must_fail git revert b1^ b1 &&
 	test_when_finished "git revert --abort" &&
 	__git_ps1 >"$actual" &&
@@ -234,7 +237,7 @@
 '
 
 test_expect_success 'prompt - bisect' '
-	printf " (master|BISECTING)" >expected &&
+	printf " (main|BISECTING)" >expected &&
 	git bisect start &&
 	test_when_finished "git bisect reset" &&
 	__git_ps1 >"$actual" &&
@@ -242,7 +245,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - clean' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
 		__git_ps1 >"$actual"
@@ -251,7 +254,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty worktree' '
-	printf " (master *)" >expected &&
+	printf " (main *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	(
@@ -262,7 +265,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index' '
-	printf " (master +)" >expected &&
+	printf " (main +)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
@@ -274,7 +277,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
-	printf " (master *+)" >expected &&
+	printf " (main *+)" >expected &&
 	echo "dirty index" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
@@ -288,7 +291,7 @@
 
 test_expect_success 'prompt - dirty status indicator - orphan branch - clean' '
 	printf " (orphan #)" >expected &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git checkout --orphan orphan &&
 	git reset --hard &&
 	(
@@ -300,7 +303,7 @@
 
 test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index' '
 	printf " (orphan +)" >expected &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git checkout --orphan orphan &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
@@ -311,7 +314,7 @@
 
 test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index and worktree' '
 	printf " (orphan *+)" >expected &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	git checkout --orphan orphan &&
 	>file &&
 	(
@@ -322,7 +325,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState false &&
@@ -334,7 +337,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState true &&
@@ -346,7 +349,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState false &&
@@ -358,7 +361,7 @@
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
-	printf " (master *)" >expected &&
+	printf " (main *)" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	test_config bash.showDirtyState true &&
@@ -382,7 +385,7 @@
 '
 
 test_expect_success 'prompt - stash status indicator - no stash' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	(
 		GIT_PS1_SHOWSTASHSTATE=y &&
 		__git_ps1 >"$actual"
@@ -391,7 +394,7 @@
 '
 
 test_expect_success 'prompt - stash status indicator - stash' '
-	printf " (master $)" >expected &&
+	printf " (main $)" >expected &&
 	echo 2 >file &&
 	git stash &&
 	test_when_finished "git stash drop" &&
@@ -417,7 +420,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - no untracked files' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
 		cd otherrepo &&
@@ -427,7 +430,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - untracked files' '
-	printf " (master %%)" >expected &&
+	printf " (main %%)" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
 		__git_ps1 >"$actual"
@@ -436,7 +439,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - empty untracked dir' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	mkdir otherrepo/untracked-dir &&
 	test_when_finished "rm -rf otherrepo/untracked-dir" &&
 	(
@@ -448,7 +451,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - non-empty untracked dir' '
-	printf " (master %%)" >expected &&
+	printf " (main %%)" >expected &&
 	mkdir otherrepo/untracked-dir &&
 	test_when_finished "rm -rf otherrepo/untracked-dir" &&
 	>otherrepo/untracked-dir/untracked-file &&
@@ -461,7 +464,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - untracked files outside cwd' '
-	printf " (master %%)" >expected &&
+	printf " (main %%)" >expected &&
 	(
 		mkdir -p ignored_dir &&
 		cd ignored_dir &&
@@ -472,7 +475,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
 		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
@@ -482,7 +485,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
 		sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
@@ -492,7 +495,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	test_config bash.showUntrackedFiles false &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -502,7 +505,7 @@
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
-	printf " (master %%)" >expected &&
+	printf " (main %%)" >expected &&
 	test_config bash.showUntrackedFiles true &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
@@ -522,13 +525,13 @@
 '
 
 test_expect_success 'prompt - format string starting with dash' '
-	printf -- "-master" >expected &&
+	printf -- "-main" >expected &&
 	__git_ps1 "-%s" >"$actual" &&
 	test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - pc mode' '
-	printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (\${__git_ps1_branch_name}):AFTER\\nmain" >expected &&
 	(
 		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
 		test_must_be_empty "$actual" &&
@@ -538,7 +541,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - branch name' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmain" >expected &&
 	(
 		GIT_PS1_SHOWCOLORHINTS=y &&
 		__git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
@@ -550,7 +553,7 @@
 test_expect_success 'prompt - bash color pc mode - detached head' '
 	printf "BEFORE: (${c_red}\${__git_ps1_branch_name}${c_clear}):AFTER\\n(%s...)" $(git log -1 --format="%h" b1^) >expected &&
 	git checkout b1^ &&
-	test_when_finished "git checkout master" &&
+	test_when_finished "git checkout main" &&
 	(
 		GIT_PS1_SHOWCOLORHINTS=y &&
 		__git_ps1 "BEFORE:" ":AFTER" &&
@@ -560,7 +563,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_clear}):AFTER\\nmain" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	(
@@ -573,7 +576,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}+${c_clear}):AFTER\\nmain" >expected &&
 	echo "dirty" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
@@ -587,7 +590,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER\\nmain" >expected &&
 	echo "dirty index" >file &&
 	test_when_finished "git reset --hard" &&
 	git add -u &&
@@ -602,7 +605,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_green}#${c_clear}):AFTER\\nmain" >expected &&
 	(
 		GIT_PS1_SHOWDIRTYSTATE=y &&
 		GIT_PS1_SHOWCOLORHINTS=y &&
@@ -628,7 +631,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - stash status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_lblue}\$${c_clear}):AFTER\\nmain" >expected &&
 	echo 2 >file &&
 	git stash &&
 	test_when_finished "git stash drop" &&
@@ -642,7 +645,7 @@
 '
 
 test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
-	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmaster" >expected &&
+	printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear} ${c_red}%%${c_clear}):AFTER\\nmain" >expected &&
 	(
 		GIT_PS1_SHOWUNTRACKEDFILES=y &&
 		GIT_PS1_SHOWCOLORHINTS=y &&
@@ -653,7 +656,7 @@
 '
 
 test_expect_success 'prompt - zsh color pc mode' '
-	printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+	printf "BEFORE: (%%F{green}main%%f):AFTER" >expected &&
 	(
 		ZSH_VERSION=5.0.0 &&
 		GIT_PS1_SHOWCOLORHINTS=y &&
@@ -664,7 +667,7 @@
 '
 
 test_expect_success 'prompt - hide if pwd ignored - env var unset, config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	test_config bash.hideIfPwdIgnored false &&
 	(
 		cd ignored_dir &&
@@ -685,7 +688,7 @@
 '
 
 test_expect_success 'prompt - hide if pwd ignored - env var unset, config unset' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	(
 		cd ignored_dir &&
 		__git_ps1 >"$actual"
@@ -704,7 +707,7 @@
 '
 
 test_expect_success 'prompt - hide if pwd ignored - env var set, config disabled' '
-	printf " (master)" >expected &&
+	printf " (main)" >expected &&
 	test_config bash.hideIfPwdIgnored false &&
 	(
 		cd ignored_dir &&
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 2f08ce7..6348e8d 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -32,11 +32,6 @@
 	export EDITOR
 }
 
-test_set_index_version () {
-    GIT_INDEX_VERSION="$1"
-    export GIT_INDEX_VERSION
-}
-
 test_decode_color () {
 	awk '
 		function name(n) {
@@ -116,13 +111,6 @@
 	tr '\015' Q | sed -e 's/Q$//'
 }
 
-# Generate an output of $1 bytes of all zeroes (NULs, not ASCII zeroes).
-# If $1 is 'infinity', output forever or until the receiving pipe stops reading,
-# whichever comes first.
-generate_zero_bytes () {
-	test-tool genzeros "$@"
-}
-
 # In some bourne shell implementations, the "unset" builtin returns
 # nonzero status when a variable to be unset was not set in the first
 # place.
@@ -178,34 +166,60 @@
 	GIT_DEBUGGER="${GIT_DEBUGGER}" "$@" <&6 >&5 2>&7
 }
 
-# Call test_commit with the arguments
-# [-C <directory>] <message> [<file> [<contents> [<tag>]]]"
+# Usage: test_commit [options] <message> [<file> [<contents> [<tag>]]]
+#   -C <dir>:
+#	Run all git commands in directory <dir>
+#   --notick
+#	Do not call test_tick before making a commit
+#   --append
+#	Use "echo >>" instead of "echo >" when writing "<contents>" to
+#	"<file>"
+#   --signoff
+#	Invoke "git commit" with --signoff
+#   --author <author>
+#	Invoke "git commit" with --author <author>
 #
 # This will commit a file with the given contents and the given commit
 # message, and tag the resulting commit with the given tag name.
 #
 # <file>, <contents>, and <tag> all default to <message>.
-#
-# If the first argument is "-C", the second argument is used as a path for
-# the git invocations.
 
 test_commit () {
 	notick= &&
+	append= &&
+	author= &&
 	signoff= &&
 	indir= &&
+	no_tag= &&
 	while test $# != 0
 	do
 		case "$1" in
 		--notick)
 			notick=yes
 			;;
+		--append)
+			append=yes
+			;;
+		--author)
+			author="$2"
+			shift
+			;;
 		--signoff)
 			signoff="$1"
 			;;
+		--date)
+			notick=yes
+			GIT_COMMITTER_DATE="$2"
+			GIT_AUTHOR_DATE="$2"
+			shift
+			;;
 		-C)
 			indir="$2"
 			shift
 			;;
+		--no-tag)
+			no_tag=yes
+			;;
 		*)
 			break
 			;;
@@ -214,14 +228,24 @@
 	done &&
 	indir=${indir:+"$indir"/} &&
 	file=${2:-"$1.t"} &&
-	echo "${3-$1}" > "$indir$file" &&
+	if test -n "$append"
+	then
+		echo "${3-$1}" >>"$indir$file"
+	else
+		echo "${3-$1}" >"$indir$file"
+	fi &&
 	git ${indir:+ -C "$indir"} add "$file" &&
 	if test -z "$notick"
 	then
 		test_tick
 	fi &&
-	git ${indir:+ -C "$indir"} commit $signoff -m "$1" &&
-	git ${indir:+ -C "$indir"} tag "${4:-$1}"
+	git ${indir:+ -C "$indir"} commit \
+	    ${author:+ --author "$author"} \
+	    $signoff -m "$1" &&
+	if test -z "$no_tag"
+	then
+		git ${indir:+ -C "$indir"} tag "${4:-$1}"
+	fi
 }
 
 # Call test_merge with the arguments "<message> <commit>", where <commit>
@@ -706,34 +730,37 @@
 }
 
 # debugging-friendly alternatives to "test [-f|-d|-e]"
-# The commands test the existence or non-existence of $1. $2 can be
-# given to provide a more precise diagnosis.
+# The commands test the existence or non-existence of $1
 test_path_is_file () {
+	test "$#" -ne 1 && BUG "1 param"
 	if ! test -f "$1"
 	then
-		echo "File $1 doesn't exist. $2"
+		echo "File $1 doesn't exist"
 		false
 	fi
 }
 
 test_path_is_dir () {
+	test "$#" -ne 1 && BUG "1 param"
 	if ! test -d "$1"
 	then
-		echo "Directory $1 doesn't exist. $2"
+		echo "Directory $1 doesn't exist"
 		false
 	fi
 }
 
 test_path_exists () {
+	test "$#" -ne 1 && BUG "1 param"
 	if ! test -e "$1"
 	then
-		echo "Path $1 doesn't exist. $2"
+		echo "Path $1 doesn't exist"
 		false
 	fi
 }
 
 # Check if the directory exists and is empty as expected, barf otherwise.
 test_dir_is_empty () {
+	test "$#" -ne 1 && BUG "1 param"
 	test_path_is_dir "$1" &&
 	if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
 	then
@@ -745,6 +772,7 @@
 
 # Check if the file exists and has a size greater than zero
 test_file_not_empty () {
+	test "$#" = 2 && BUG "2 param"
 	if ! test -s "$1"
 	then
 		echo "'$1' is not a non-empty file."
@@ -753,6 +781,7 @@
 }
 
 test_path_is_missing () {
+	test "$#" -ne 1 && BUG "1 param"
 	if test -e "$1"
 	then
 		echo "Path exists:"
@@ -789,6 +818,7 @@
 }
 
 test_file_size () {
+	test "$#" -ne 1 && BUG "1 param"
 	test-tool path-utils file-size "$1"
 }
 
@@ -961,6 +991,7 @@
 # - not all diff versions understand "-u"
 
 test_cmp () {
+	test "$#" -ne 2 && BUG "2 param"
 	eval "$GIT_TEST_CMP" '"$@"'
 }
 
@@ -990,22 +1021,20 @@
 # test_cmp_bin - helper to compare binary files
 
 test_cmp_bin () {
+	test "$#" -ne 2 && BUG "2 param"
 	cmp "$@"
 }
 
-# Use this instead of test_cmp to compare files that contain expected and
-# actual output from git commands that can be translated.  When running
-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected
-# results.
+# Wrapper for test_cmp which used to be used for
+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
+# in-flight changes. Should not be used and will be removed soon.
 test_i18ncmp () {
-	! test_have_prereq C_LOCALE_OUTPUT || test_cmp "$@"
+	test_cmp "$@"
 }
 
-# Use this instead of "grep expected-string actual" to see if the
-# output from a git command that can be translated either contains an
-# expected string, or does not contain an unwanted one.  When running
-# under GIT_TEST_GETTEXT_POISON this pretends that the command produced expected
-# results.
+# Wrapper for grep which used to be used for
+# GIT_TEST_GETTEXT_POISON=false. Only here as a shim for other
+# in-flight changes. Should not be used and will be removed soon.
 test_i18ngrep () {
 	eval "last_arg=\${$#}"
 
@@ -1018,12 +1047,6 @@
 		BUG "too few parameters to test_i18ngrep"
 	fi
 
-	if test_have_prereq !C_LOCALE_OUTPUT
-	then
-		# pretend success
-		return 0
-	fi
-
 	if test "x!" = "x$1"
 	then
 		shift
@@ -1059,6 +1082,7 @@
 # otherwise.
 
 test_must_be_empty () {
+	test "$#" -ne 1 && BUG "1 param"
 	test_path_is_file "$1" &&
 	if test -s "$1"
 	then
@@ -1082,7 +1106,7 @@
 	fi
 	if test $# != 2
 	then
-		error "bug in the test script: test_cmp_rev requires two revisions, but got $#"
+		BUG "test_cmp_rev requires two revisions, but got $#"
 	else
 		local r1 r2
 		r1=$(git rev-parse --verify "$1") &&
@@ -1193,7 +1217,7 @@
 	# doing so on Bash is better than nothing (the test will
 	# silently pass on other shells).
 	test "${BASH_SUBSHELL-0}" = 0 ||
-	error "bug in test script: test_atexit does nothing in a subshell"
+	BUG "test_atexit does nothing in a subshell"
 	test_atexit_cleanup="{ $*
 		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
 }
@@ -1591,33 +1615,6 @@
 	eval $var=$port
 }
 
-# Compare a file containing rev-list bitmap traversal output to its non-bitmap
-# counterpart. You can't just use test_cmp for this, because the two produce
-# subtly different output:
-#
-#   - regular output is in traversal order, whereas bitmap is split by type,
-#     with non-packed objects at the end
-#
-#   - regular output has a space and the pathname appended to non-commit
-#     objects; bitmap output omits this
-#
-# This function normalizes and compares the two. The second file should
-# always be the bitmap output.
-test_bitmap_traversal () {
-	if test "$1" = "--no-confirm-bitmaps"
-	then
-		shift
-	elif cmp "$1" "$2"
-	then
-		echo >&2 "identical raw outputs; are you sure bitmaps were used?"
-		return 1
-	fi &&
-	cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
-	sort "$2" >"$2.normalized" &&
-	test_cmp "$1.normalized" "$2.normalized" &&
-	rm -f "$1.normalized" "$2.normalized"
-}
-
 # Tests for the hidden file attribute on Windows
 test_path_is_hidden () {
 	test_have_prereq MINGW ||
@@ -1660,3 +1657,45 @@
 		grep "\[$expr\]"
 	fi
 }
+
+# Check that the given command was invoked as part of the
+# trace2-format trace on stdin.
+#
+#	test_region [!] <category> <label> git <command> <args>...
+#
+# For example, to look for trace2_region_enter("index", "do_read_index", repo)
+# in an invocation of "git checkout HEAD~1", run
+#
+#	GIT_TRACE2_EVENT="$(pwd)/trace.txt" GIT_TRACE2_EVENT_NESTING=10 \
+#		git checkout HEAD~1 &&
+#	test_region index do_read_index <trace.txt
+#
+# If the first parameter passed is !, this instead checks that
+# the given region was not entered.
+#
+test_region () {
+	local expect_exit=0
+	if test "$1" = "!"
+	then
+		expect_exit=1
+		shift
+	fi
+
+	grep -e	'"region_enter".*"category":"'"$1"'","label":"'"$2"\" "$3"
+	exitcode=$?
+
+	if test $exitcode != $expect_exit
+	then
+		return 1
+	fi
+
+	grep -e	'"region_leave".*"category":"'"$1"'","label":"'"$2"\" "$3"
+	exitcode=$?
+
+	if test $exitcode != $expect_exit
+	then
+		return 1
+	fi
+
+	return 0
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 9fa7c1d..d3f6af6 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -163,8 +163,8 @@
 		;;
 	--stress-jobs=*)
 		stress=t;
-		stress=${opt#--*=}
-		case "$stress" in
+		stress_jobs=${opt#--*=}
+		case "$stress_jobs" in
 		*[!0-9]*|0*|"")
 			echo "error: --stress-jobs=<N> requires the number of jobs to run" >&2
 			exit 1
@@ -262,9 +262,9 @@
 	: # Don't stress test again.
 elif test -n "$stress"
 then
-	if test "$stress" != t
+	if test -n "$stress_jobs"
 	then
-		job_count=$stress
+		job_count=$stress_jobs
 	elif test -n "$GIT_TEST_STRESS_LOAD"
 	then
 		job_count="$GIT_TEST_STRESS_LOAD"
@@ -404,15 +404,6 @@
 export LANG LC_ALL PAGER TZ
 EDITOR=:
 
-# GIT_TEST_GETTEXT_POISON should not influence git commands executed
-# during initialization of test-lib and the test repo. Back it up,
-# unset and then restore after initialization is finished.
-if test -n "$GIT_TEST_GETTEXT_POISON"
-then
-	GIT_TEST_GETTEXT_POISON_ORIG=$GIT_TEST_GETTEXT_POISON
-	unset GIT_TEST_GETTEXT_POISON
-fi
-
 # A call to "unset" with no arguments causes at least Solaris 10
 # /usr/xpg4/bin/sh and /bin/ksh to bail out.  So keep the unsets
 # deriving from the command substitution clustered with the other
@@ -462,36 +453,6 @@
 GIT_TRACE_BARE=1
 export GIT_TRACE_BARE
 
-check_var_migration () {
-	# the warnings and hints given from this helper depends
-	# on end-user settings, which will disrupt the self-test
-	# done on the test framework itself.
-	case "$GIT_TEST_FRAMEWORK_SELFTEST" in
-	t)	return ;;
-	esac
-
-	old_name=$1 new_name=$2
-	eval "old_isset=\${${old_name}:+isset}"
-	eval "new_isset=\${${new_name}:+isset}"
-
-	case "$old_isset,$new_isset" in
-	isset,)
-		echo >&2 "warning: $old_name is now $new_name"
-		echo >&2 "hint: set $new_name too during the transition period"
-		eval "$new_name=\$$old_name"
-		;;
-	isset,isset)
-		# do this later
-		# echo >&2 "warning: $old_name is now $new_name"
-		# echo >&2 "hint: remove $old_name"
-		;;
-	esac
-}
-
-check_var_migration GIT_FSMONITOR_TEST GIT_TEST_FSMONITOR
-check_var_migration TEST_GIT_INDEX_VERSION GIT_TEST_INDEX_VERSION
-check_var_migration GIT_FORCE_PRELOAD_TEST GIT_TEST_PRELOAD_INDEX
-
 # Use specific version of the index file format
 if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
 then
@@ -1524,22 +1485,10 @@
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
-test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE
-test -n "$USE_LIBPCRE1" && test_set_prereq LIBPCRE1
+test -n "$USE_LIBPCRE2" && test_set_prereq PCRE
 test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
 test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
 
-if test -n "$GIT_TEST_GETTEXT_POISON_ORIG"
-then
-	GIT_TEST_GETTEXT_POISON=$GIT_TEST_GETTEXT_POISON_ORIG
-	export GIT_TEST_GETTEXT_POISON
-	unset GIT_TEST_GETTEXT_POISON_ORIG
-fi
-
-test_lazy_prereq C_LOCALE_OUTPUT '
-	! test_bool_env GIT_TEST_GETTEXT_POISON false
-'
-
 if test -z "$GIT_TEST_CHECK_CACHE_TREE"
 then
 	GIT_TEST_CHECK_CACHE_TREE=true
@@ -1713,7 +1662,8 @@
 '
 
 # Ensure that no test accidentally triggers a Git command
-# that runs 'crontab', affecting a user's cron schedule.
-# Tests that verify the cron integration must set this locally
+# that runs the actual maintenance scheduler, affecting a user's
+# system permanently.
+# Tests that verify the scheduler integration must set this locally
 # to avoid errors.
-GIT_TEST_CRONTAB="exit 1"
+GIT_TEST_MAINT_SCHEDULER="none:exit 1"
diff --git a/tag.c b/tag.c
index 1ed2684..3e18a41 100644
--- a/tag.c
+++ b/tag.c
@@ -13,26 +13,27 @@
 static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
 {
 	struct signature_check sigc;
-	size_t payload_size;
+	struct strbuf payload = STRBUF_INIT;
+	struct strbuf signature = STRBUF_INIT;
 	int ret;
 
 	memset(&sigc, 0, sizeof(sigc));
 
-	payload_size = parse_signature(buf, size);
-
-	if (size == payload_size) {
+	if (!parse_signature(buf, size, &payload, &signature)) {
 		if (flags & GPG_VERIFY_VERBOSE)
-			write_in_full(1, buf, payload_size);
+			write_in_full(1, buf, size);
 		return error("no signature found");
 	}
 
-	ret = check_signature(buf, payload_size, buf + payload_size,
-				size - payload_size, &sigc);
+	ret = check_signature(payload.buf, payload.len, signature.buf,
+				signature.len, &sigc);
 
 	if (!(flags & GPG_VERIFY_OMIT_STATUS))
 		print_signature_buffer(&sigc, flags);
 
 	signature_check_clear(&sigc);
+	strbuf_release(&payload);
+	strbuf_release(&signature);
 	return ret;
 }
 
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 42ed4db..b8d880e 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -185,9 +185,11 @@
 		return 1;
 	if (ends_with(name, ".pack"))
 		return 2;
-	if (ends_with(name, ".idx"))
+	if (ends_with(name, ".rev"))
 		return 3;
-	return 4;
+	if (ends_with(name, ".idx"))
+		return 4;
+	return 5;
 }
 
 static int pack_copy_cmp(const char *a, const char *b)
diff --git a/trailer.c b/trailer.c
index 3f7391d..249ed61 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1131,7 +1131,9 @@
 	size_t i;
 
 	/* If we want the whole block untouched, we can take the fast path. */
-	if (!opts->only_trailers && !opts->unfold && !opts->filter && !opts->separator) {
+	if (!opts->only_trailers && !opts->unfold && !opts->filter &&
+	    !opts->separator && !opts->key_only && !opts->value_only &&
+	    !opts->key_value_separator) {
 		strbuf_add(out, info->trailer_start,
 			   info->trailer_end - info->trailer_start);
 		return;
@@ -1153,8 +1155,15 @@
 				if (opts->separator && out->len != origlen)
 					strbuf_addbuf(out, opts->separator);
 				if (!opts->value_only)
-					strbuf_addf(out, "%s: ", tok.buf);
-				strbuf_addbuf(out, &val);
+					strbuf_addbuf(out, &tok);
+				if (!opts->key_only && !opts->value_only) {
+					if (opts->key_value_separator)
+						strbuf_addbuf(out, opts->key_value_separator);
+					else
+						strbuf_addstr(out, ": ");
+				}
+				if (!opts->key_only)
+					strbuf_addbuf(out, &val);
 				if (!opts->separator)
 					strbuf_addch(out, '\n');
 			}
diff --git a/trailer.h b/trailer.h
index cd93e7d..795d2fc 100644
--- a/trailer.h
+++ b/trailer.h
@@ -71,8 +71,10 @@
 	int only_input;
 	int unfold;
 	int no_divider;
+	int key_only;
 	int value_only;
 	const struct strbuf *separator;
+	const struct strbuf *key_value_separator;
 	int (*filter)(const struct strbuf *, void *);
 	void *filter_data;
 };
diff --git a/transport-helper.c b/transport-helper.c
index 5f6e0b3..49b7fb4 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1162,13 +1162,14 @@
 }
 
 static struct ref *get_refs_list(struct transport *transport, int for_push,
-				 const struct strvec *ref_prefixes)
+				 struct transport_ls_refs_options *transport_options)
 {
 	get_helper(transport);
 
 	if (process_connect(transport, for_push)) {
 		do_take_over(transport);
-		return transport->vtable->get_refs_list(transport, for_push, ref_prefixes);
+		return transport->vtable->get_refs_list(transport, for_push,
+							transport_options);
 	}
 
 	return get_refs_list_using_list(transport, for_push);
diff --git a/transport-internal.h b/transport-internal.h
index 27c9daf..b60f1ba 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -4,6 +4,7 @@
 struct ref;
 struct transport;
 struct strvec;
+struct transport_ls_refs_options;
 
 struct transport_vtable {
 	/**
@@ -18,19 +19,12 @@
 	 * the transport to try to share connections, for_push is a
 	 * hint as to whether the ultimate operation is a push or a fetch.
 	 *
-	 * If communicating using protocol v2 a list of prefixes can be
-	 * provided to be sent to the server to enable it to limit the ref
-	 * advertisement.  Since ref filtering is done on the server's end, and
-	 * only when using protocol v2, this list will be ignored when not
-	 * using protocol v2 meaning this function can return refs which don't
-	 * match the provided ref_prefixes.
-	 *
 	 * If the transport is able to determine the remote hash for
 	 * the ref without a huge amount of effort, it should store it
 	 * in the ref's old_sha1 field; otherwise it should be all 0.
 	 **/
 	struct ref *(*get_refs_list)(struct transport *transport, int for_push,
-				     const struct strvec *ref_prefixes);
+				     struct transport_ls_refs_options *transport_options);
 
 	/**
 	 * Fetch the objects for the given refs. Note that this gets
diff --git a/transport.c b/transport.c
index 679a35e..b13fab5 100644
--- a/transport.c
+++ b/transport.c
@@ -127,7 +127,7 @@
 
 static struct ref *get_refs_from_bundle(struct transport *transport,
 					int for_push,
-					const struct strvec *ref_prefixes)
+					struct transport_ls_refs_options *transport_options)
 {
 	struct bundle_transport_data *data = transport->data;
 	struct ref *result = NULL;
@@ -280,7 +280,7 @@
  * remote refs.
  */
 static struct ref *handshake(struct transport *transport, int for_push,
-			     const struct strvec *ref_prefixes,
+			     struct transport_ls_refs_options *options,
 			     int must_list_refs)
 {
 	struct git_transport_data *data = transport->data;
@@ -303,7 +303,7 @@
 			trace2_data_string("transfer", NULL, "server-sid", server_sid);
 		if (must_list_refs)
 			get_remote_refs(data->fd[1], &reader, &refs, for_push,
-					ref_prefixes,
+					options,
 					transport->server_options,
 					transport->stateless_rpc);
 		break;
@@ -334,9 +334,9 @@
 }
 
 static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
-					const struct strvec *ref_prefixes)
+					struct transport_ls_refs_options *options)
 {
-	return handshake(transport, for_push, ref_prefixes, 1);
+	return handshake(transport, for_push, options, 1);
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
@@ -1252,19 +1252,20 @@
 		int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
 		int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
 		int push_ret, ret, err;
-		struct strvec ref_prefixes = STRVEC_INIT;
+		struct transport_ls_refs_options transport_options =
+			TRANSPORT_LS_REFS_OPTIONS_INIT;
 
 		if (check_push_refs(local_refs, rs) < 0)
 			return -1;
 
-		refspec_ref_prefixes(rs, &ref_prefixes);
+		refspec_ref_prefixes(rs, &transport_options.ref_prefixes);
 
 		trace2_region_enter("transport_push", "get_refs_list", r);
 		remote_refs = transport->vtable->get_refs_list(transport, 1,
-							       &ref_prefixes);
+							       &transport_options);
 		trace2_region_leave("transport_push", "get_refs_list", r);
 
-		strvec_clear(&ref_prefixes);
+		strvec_clear(&transport_options.ref_prefixes);
 
 		if (flags & TRANSPORT_PUSH_ALL)
 			match_flags |= MATCH_REFS_ALL;
@@ -1380,12 +1381,12 @@
 }
 
 const struct ref *transport_get_remote_refs(struct transport *transport,
-					    const struct strvec *ref_prefixes)
+					    struct transport_ls_refs_options *transport_options)
 {
 	if (!transport->got_remote_refs) {
 		transport->remote_refs =
 			transport->vtable->get_refs_list(transport, 0,
-							 ref_prefixes);
+							 transport_options);
 		transport->got_remote_refs = 1;
 	}
 
diff --git a/transport.h b/transport.h
index 24558c0..24e1579 100644
--- a/transport.h
+++ b/transport.h
@@ -233,17 +233,32 @@
 		   struct refspec *rs, int flags,
 		   unsigned int * reject_reasons);
 
+struct transport_ls_refs_options {
+	/*
+	 * Optionally, a list of ref prefixes can be provided which can be sent
+	 * to the server (when communicating using protocol v2) to enable it to
+	 * limit the ref advertisement.  Since ref filtering is done on the
+	 * server's end (and only when using protocol v2),
+	 * transport_get_remote_refs() could return refs which don't match the
+	 * provided ref_prefixes.
+	 */
+	struct strvec ref_prefixes;
+
+	/*
+	 * If unborn_head_target is not NULL, and the remote reports HEAD as
+	 * pointing to an unborn branch, transport_get_remote_refs() stores the
+	 * unborn branch in unborn_head_target. It should be freed by the
+	 * caller.
+	 */
+	char *unborn_head_target;
+};
+#define TRANSPORT_LS_REFS_OPTIONS_INIT { STRVEC_INIT }
+
 /*
  * Retrieve refs from a remote.
- *
- * Optionally a list of ref prefixes can be provided which can be sent to the
- * server (when communicating using protocol v2) to enable it to limit the ref
- * advertisement.  Since ref filtering is done on the server's end (and only
- * when using protocol v2), this can return refs which don't match the provided
- * ref_prefixes.
  */
 const struct ref *transport_get_remote_refs(struct transport *transport,
-					    const struct strvec *ref_prefixes);
+					    struct transport_ls_refs_options *transport_options);
 
 /*
  * Fetch the hash algorithm used by a remote.
diff --git a/tree-walk.c b/tree-walk.c
index 0160294..2d6226d 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -4,6 +4,7 @@
 #include "object-store.h"
 #include "tree.h"
 #include "pathspec.h"
+#include "json-writer.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
 {
@@ -167,6 +168,25 @@
 	return 1;
 }
 
+static int traverse_trees_atexit_registered;
+static int traverse_trees_count;
+static int traverse_trees_cur_depth;
+static int traverse_trees_max_depth;
+
+static void trace2_traverse_trees_statistics_atexit(void)
+{
+	struct json_writer jw = JSON_WRITER_INIT;
+
+	jw_object_begin(&jw, 0);
+	jw_object_intmax(&jw, "traverse_trees_count", traverse_trees_count);
+	jw_object_intmax(&jw, "traverse_trees_max_depth", traverse_trees_max_depth);
+	jw_end(&jw);
+
+	trace2_data_json("traverse_trees", the_repository, "statistics", &jw);
+
+	jw_release(&jw);
+}
+
 void setup_traverse_info(struct traverse_info *info, const char *base)
 {
 	size_t pathlen = strlen(base);
@@ -180,6 +200,11 @@
 	info->namelen = pathlen;
 	if (pathlen)
 		info->prev = &dummy;
+
+	if (trace2_is_enabled() && !traverse_trees_atexit_registered) {
+		atexit(trace2_traverse_trees_statistics_atexit);
+		traverse_trees_atexit_registered = 1;
+	}
 }
 
 char *make_traverse_path(char *path, size_t pathlen,
@@ -416,6 +441,12 @@
 	int interesting = 1;
 	char *traverse_path;
 
+	traverse_trees_count++;
+	traverse_trees_cur_depth++;
+
+	if (traverse_trees_cur_depth > traverse_trees_max_depth)
+		traverse_trees_max_depth = traverse_trees_cur_depth;
+
 	if (n >= ARRAY_SIZE(entry))
 		BUG("traverse_trees() called with too many trees (%d)", n);
 
@@ -515,6 +546,8 @@
 	free(traverse_path);
 	info->traverse_path = NULL;
 	strbuf_release(&base);
+
+	traverse_trees_cur_depth--;
 	return error;
 }
 
diff --git a/tree.c b/tree.c
index e76517f..a524798 100644
--- a/tree.c
+++ b/tree.c
@@ -144,7 +144,7 @@
 	return ret;
 }
 
-static int cmp_cache_name_compare(const void *a_, const void *b_)
+int cmp_cache_name_compare(const void *a_, const void *b_)
 {
 	const struct cache_entry *ce1, *ce2;
 
diff --git a/tree.h b/tree.h
index 9383745..3eb0484 100644
--- a/tree.h
+++ b/tree.h
@@ -28,6 +28,8 @@
 /* Parses and returns the tree in the given ent, chasing tags and commits. */
 struct tree *parse_tree_indirect(const struct object_id *oid);
 
+int cmp_cache_name_compare(const void *a_, const void *b_);
+
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, int, void *);
 
diff --git a/unpack-trees.c b/unpack-trees.c
index 2344b5e..eb8fcda 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1552,14 +1552,10 @@
 static void populate_from_existing_patterns(struct unpack_trees_options *o,
 					    struct pattern_list *pl)
 {
-	char *sparse = git_pathdup("info/sparse-checkout");
-
-	pl->use_cone_patterns = core_sparse_checkout_cone;
-	if (add_patterns_from_file_to_list(sparse, "", 0, pl, NULL) < 0)
+	if (get_sparse_checkout_patterns(pl) < 0)
 		o->skip_sparse_checkout = 1;
 	else
 		o->pl = pl;
-	free(sparse);
 }
 
 
@@ -1583,6 +1579,8 @@
 		die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
 
 	trace_performance_enter();
+	trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
+
 	if (!core_apply_sparse_checkout || !o->update)
 		o->skip_sparse_checkout = 1;
 	if (!o->skip_sparse_checkout && !o->pl) {
@@ -1656,7 +1654,9 @@
 		}
 
 		trace_performance_enter();
+		trace2_region_enter("unpack_trees", "traverse_trees", the_repository);
 		ret = traverse_trees(o->src_index, len, t, &info);
+		trace2_region_leave("unpack_trees", "traverse_trees", the_repository);
 		trace_performance_leave("traverse_trees");
 		if (ret < 0)
 			goto return_failed;
@@ -1725,8 +1725,6 @@
 		if (!ret) {
 			if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
 				cache_tree_verify(the_repository, &o->result);
-			if (!o->result.cache_tree)
-				o->result.cache_tree = cache_tree();
 			if (!cache_tree_fully_valid(o->result.cache_tree))
 				cache_tree_update(&o->result,
 						  WRITE_TREE_SILENT |
@@ -1744,6 +1742,7 @@
 done:
 	if (free_pattern_list)
 		clear_pattern_list(&pl);
+	trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
 	trace_performance_leave("unpack_trees");
 	return ret;
 
diff --git a/upload-pack.c b/upload-pack.c
index 3b66bf9..e19583a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -305,14 +305,7 @@
 	if (pack_data->filter_options.choice) {
 		const char *spec =
 			expand_list_objects_filter_spec(&pack_data->filter_options);
-		if (pack_objects.use_shell) {
-			struct strbuf buf = STRBUF_INIT;
-			sq_quote_buf(&buf, spec);
-			strvec_pushf(&pack_objects.args, "--filter=%s", buf.buf);
-			strbuf_release(&buf);
-		} else {
-			strvec_pushf(&pack_objects.args, "--filter=%s", spec);
-		}
+		strvec_pushf(&pack_objects.args, "--filter=%s", spec);
 	}
 	if (uri_protocols) {
 		for (i = 0; i < uri_protocols->nr; i++)
@@ -500,7 +493,7 @@
 
 static int ok_to_give_up(struct upload_pack_data *data)
 {
-	uint32_t min_generation = GENERATION_NUMBER_ZERO;
+	timestamp_t min_generation = GENERATION_NUMBER_ZERO;
 
 	if (!data->have_obj.nr)
 		return 0;
@@ -1232,7 +1225,7 @@
 		packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
 	}
 	capabilities = NULL;
-	if (!peel_ref(refname, &peeled))
+	if (!peel_iterated_oid(oid, &peeled))
 		packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
 	return 0;
 }
diff --git a/usage.c b/usage.c
index 1868a24..1b206de 100644
--- a/usage.c
+++ b/usage.c
@@ -266,6 +266,10 @@
 static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
 {
 	char prefix[256];
+	va_list params_copy;
+	static int in_bug;
+
+	va_copy(params_copy, params);
 
 	/* truncation via snprintf is OK here */
 	if (file)
@@ -274,6 +278,13 @@
 		snprintf(prefix, sizeof(prefix), "BUG: ");
 
 	vreportf(prefix, fmt, params);
+
+	if (in_bug)
+		abort();
+	in_bug = 1;
+
+	trace2_cmd_error_va(fmt, params_copy);
+
 	if (BUG_exit_code)
 		exit(BUG_exit_code);
 	abort();
diff --git a/worktree.c b/worktree.c
index f84ceae..e008585 100644
--- a/worktree.c
+++ b/worktree.c
@@ -15,6 +15,7 @@
 		free(worktrees[i]->id);
 		free(worktrees[i]->head_ref);
 		free(worktrees[i]->lock_reason);
+		free(worktrees[i]->prune_reason);
 		free(worktrees[i]);
 	}
 	free (worktrees);
@@ -224,7 +225,8 @@
 
 const char *worktree_lock_reason(struct worktree *wt)
 {
-	assert(!is_main_worktree(wt));
+	if (is_main_worktree(wt))
+		return NULL;
 
 	if (!wt->lock_reason_valid) {
 		struct strbuf path = STRBUF_INIT;
@@ -245,6 +247,25 @@
 	return wt->lock_reason;
 }
 
+const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
+{
+	struct strbuf reason = STRBUF_INIT;
+	char *path = NULL;
+
+	if (is_main_worktree(wt))
+		return NULL;
+	if (wt->prune_reason_valid)
+		return wt->prune_reason;
+
+	if (should_prune_worktree(wt->id, &reason, &path, expire))
+		wt->prune_reason = strbuf_detach(&reason, NULL);
+	wt->prune_reason_valid = 1;
+
+	strbuf_release(&reason);
+	free(path);
+	return wt->prune_reason;
+}
+
 /* convenient wrapper to deal with NULL strbuf */
 static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
 {
@@ -645,6 +666,42 @@
 }
 
 /*
+ * If both the main worktree and linked worktree have been moved, then the
+ * gitfile /path/to/worktree/.git won't point into the repository, thus we
+ * won't know which <repo>/worktrees/<id>/gitdir to repair. However, we may
+ * be able to infer the gitdir by manually reading /path/to/worktree/.git,
+ * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
+ */
+static char *infer_backlink(const char *gitfile)
+{
+	struct strbuf actual = STRBUF_INIT;
+	struct strbuf inferred = STRBUF_INIT;
+	const char *id;
+
+	if (strbuf_read_file(&actual, gitfile, 0) < 0)
+		goto error;
+	if (!starts_with(actual.buf, "gitdir:"))
+		goto error;
+	if (!(id = find_last_dir_sep(actual.buf)))
+		goto error;
+	strbuf_trim(&actual);
+	id++; /* advance past '/' to point at <id> */
+	if (!*id)
+		goto error;
+	strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
+	if (!is_directory(inferred.buf))
+		goto error;
+
+	strbuf_release(&actual);
+	return strbuf_detach(&inferred, NULL);
+
+error:
+	strbuf_release(&actual);
+	strbuf_release(&inferred);
+	return NULL;
+}
+
+/*
  * Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
  * the worktree's path.
  */
@@ -675,6 +732,11 @@
 	if (err == READ_GITFILE_ERR_NOT_A_FILE) {
 		fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
 		goto done;
+	} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
+		if (!(backlink = infer_backlink(realdotgit.buf))) {
+			fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
+			goto done;
+		}
 	} else if (err) {
 		fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
 		goto done;
@@ -700,3 +762,71 @@
 	strbuf_release(&realdotgit);
 	strbuf_release(&dotgit);
 }
+
+int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
+{
+	struct stat st;
+	char *path;
+	int fd;
+	size_t len;
+	ssize_t read_result;
+
+	*wtpath = NULL;
+	if (!is_directory(git_path("worktrees/%s", id))) {
+		strbuf_addstr(reason, _("not a valid directory"));
+		return 1;
+	}
+	if (file_exists(git_path("worktrees/%s/locked", id)))
+		return 0;
+	if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
+		strbuf_addstr(reason, _("gitdir file does not exist"));
+		return 1;
+	}
+	fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
+	if (fd < 0) {
+		strbuf_addf(reason, _("unable to read gitdir file (%s)"),
+			    strerror(errno));
+		return 1;
+	}
+	len = xsize_t(st.st_size);
+	path = xmallocz(len);
+
+	read_result = read_in_full(fd, path, len);
+	if (read_result < 0) {
+		strbuf_addf(reason, _("unable to read gitdir file (%s)"),
+			    strerror(errno));
+		close(fd);
+		free(path);
+		return 1;
+	}
+	close(fd);
+
+	if (read_result != len) {
+		strbuf_addf(reason,
+			    _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
+			    (uintmax_t)len, (uintmax_t)read_result);
+		free(path);
+		return 1;
+	}
+	while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
+		len--;
+	if (!len) {
+		strbuf_addstr(reason, _("invalid gitdir file"));
+		free(path);
+		return 1;
+	}
+	path[len] = '\0';
+	if (!file_exists(path)) {
+		if (stat(git_path("worktrees/%s/index", id), &st) ||
+		    st.st_mtime <= expire) {
+			strbuf_addstr(reason, _("gitdir file points to non-existent location"));
+			free(path);
+			return 1;
+		} else {
+			*wtpath = path;
+			return 0;
+		}
+	}
+	*wtpath = path;
+	return 0;
+}
diff --git a/worktree.h b/worktree.h
index f38e6fd..8b7c408 100644
--- a/worktree.h
+++ b/worktree.h
@@ -11,11 +11,13 @@
 	char *id;
 	char *head_ref;		/* NULL if HEAD is broken or detached */
 	char *lock_reason;	/* private - use worktree_lock_reason */
+	char *prune_reason;     /* private - use worktree_prune_reason */
 	struct object_id head_oid;
 	int is_detached;
 	int is_bare;
 	int is_current;
 	int lock_reason_valid; /* private */
+	int prune_reason_valid; /* private */
 };
 
 /*
@@ -73,6 +75,27 @@
  */
 const char *worktree_lock_reason(struct worktree *wt);
 
+/*
+ * Return the reason string if the given worktree should be pruned, otherwise
+ * NULL if it should not be pruned. `expire` defines a grace period to prune
+ * the worktree when its path does not exist.
+ */
+const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire);
+
+/*
+ * Return true if worktree entry should be pruned, along with the reason for
+ * pruning. Otherwise, return false and the worktree's path in `wtpath`, or
+ * NULL if it cannot be determined. Caller is responsible for freeing
+ * returned path.
+ *
+ * `expire` defines a grace period to prune the worktree when its path
+ * does not exist.
+ */
+int should_prune_worktree(const char *id,
+			  struct strbuf *reason,
+			  char **wtpath,
+			  timestamp_t expire);
+
 #define WT_VALIDATE_WORKTREE_MISSING_OK (1 << 0)
 
 /*
diff --git a/wt-status.c b/wt-status.c
index 40b59be..0c8287a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -606,7 +606,9 @@
 	if (s->ignore_submodule_arg) {
 		rev.diffopt.flags.override_submodule_config = 1;
 		handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
-	}
+	} else if (!rev.diffopt.flags.ignore_submodule_set &&
+			s->show_untracked_files != SHOW_NO_UNTRACKED_FILES)
+		handle_ignore_submodules_arg(&rev.diffopt, "none");
 	rev.diffopt.format_callback = wt_status_collect_changed_cb;
 	rev.diffopt.format_callback_data = s;
 	rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;