Merge branch 'jc/varargs-attributes'

Varargs functions that are unannotated as printf-like or execl-like
have been annotated as such.

* jc/varargs-attributes:
  __attribute__: add a few missing format attributes
  __attribute__: mark some functions with LAST_ARG_MUST_BE_NULL
  __attribute__: remove redundant attribute declaration for git_die_config()
  __attribute__: trace2_region_enter_printf() is like "printf"
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index a241a63..d0a78fc 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -26,66 +26,7 @@
     - name: git log --check
       id: check_out
       run: |
-        baseSha=${{github.event.pull_request.base.sha}}
-        problems=()
-        commit=
-        commitText=
-        commitTextmd=
-        goodparent=
-        while read dash sha etc
-        do
-          case "${dash}" in
-          "---")
-            if test -z "${commit}"
-            then
-              goodparent=${sha}
-            fi
-            commit="${sha}"
-            commitText="${sha} ${etc}"
-            commitTextmd="[${sha}](https://github.com/${{ github.repository }}/commit/${sha}) ${etc}"
-            ;;
-          "")
-            ;;
-          *)
-            if test -n "${commit}"
-            then
-              problems+=("1) --- ${commitTextmd}")
-              echo ""
-              echo "--- ${commitText}"
-              commit=
-            fi
-            case "${dash}" in
-            *:[1-9]*:) # contains file and line number information
-              dashend=${dash#*:}
-              problems+=("[${dash}](https://github.com/${{ github.repository }}/blob/${{github.event.pull_request.head.ref}}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
-              ;;
-            *)
-              problems+=("\`${dash} ${sha} ${etc}\`")
-              ;;
-            esac
-            echo "${dash} ${sha} ${etc}"
-            ;;
-          esac
-        done <<< $(git log --check --pretty=format:"---% h% s" ${baseSha}..)
-
-        if test ${#problems[*]} -gt 0
-        then
-          if test -z "${commit}"
-          then
-            goodparent=${baseSha: 0:7}
-          fi
-          echo "🛑 Please review the Summary output for further information."
-          echo "### :x: A whitespace issue was found in one or more of the commits." >$GITHUB_STEP_SUMMARY
-          echo "" >>$GITHUB_STEP_SUMMARY
-          echo "Run these commands to correct the problem:" >>$GITHUB_STEP_SUMMARY
-          echo "1. \`git rebase --whitespace=fix ${goodparent}\`" >>$GITHUB_STEP_SUMMARY
-          echo "1. \`git push --force\`" >>$GITHUB_STEP_SUMMARY
-          echo " " >>$GITHUB_STEP_SUMMARY
-          echo "Errors:" >>$GITHUB_STEP_SUMMARY
-          for i in "${problems[@]}"
-          do
-            echo "${i}" >>$GITHUB_STEP_SUMMARY
-          done
-
-          exit 2
-        fi
+        ./ci/check-whitespace.sh \
+          "${{github.event.pull_request.base.sha}}" \
+          "$GITHUB_STEP_SUMMARY" \
+          "https://github.com/${{github.repository}}"
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
index 53cf12f..48341e8 100644
--- a/.github/workflows/coverity.yml
+++ b/.github/workflows/coverity.yml
@@ -45,7 +45,7 @@
       - run: ci/install-dependencies.sh
         if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
         env:
-          runs_on_pool: ${{ matrix.os }}
+          distro: ${{ matrix.os }}
 
       # The Coverity site says the tool is usually updated twice yearly, so the
       # MD5 of download can be used to determine whether there's been an update.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3428773..13cc0fe 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -284,8 +284,7 @@
             cc: clang
             pool: macos-13
           - jobname: osx-gcc
-            cc: gcc
-            cc_package: gcc-13
+            cc: gcc-13
             pool: macos-13
           - jobname: linux-gcc-default
             cc: gcc
@@ -303,7 +302,7 @@
       CC: ${{matrix.vector.cc}}
       CC_PACKAGE: ${{matrix.vector.cc_package}}
       jobname: ${{matrix.vector.jobname}}
-      runs_on_pool: ${{matrix.vector.pool}}
+      distro: ${{matrix.vector.pool}}
     runs-on: ${{matrix.vector.pool}}
     steps:
     - uses: actions/checkout@v4
@@ -342,12 +341,16 @@
         vector:
         - jobname: linux-musl
           image: alpine
+          distro: alpine-latest
         - jobname: linux32
           image: daald/ubuntu32:xenial
+          distro: ubuntu32-16.04
         - jobname: pedantic
           image: fedora
+          distro: fedora-latest
     env:
       jobname: ${{matrix.vector.jobname}}
+      distro: ${{matrix.vector.distro}}
     runs-on: ubuntu-latest
     container: ${{matrix.vector.image}}
     steps:
@@ -355,7 +358,7 @@
       if: matrix.vector.jobname != 'linux32'
     - uses: actions/checkout@v1 # cannot be upgraded because Node.js Actions aren't supported in this container
       if: matrix.vector.jobname == 'linux32'
-    - run: ci/install-docker-dependencies.sh
+    - run: ci/install-dependencies.sh
     - run: ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
diff --git a/.gitignore b/.gitignore
index 612c0f6..8caf370 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,6 +126,7 @@
 /git-rebase
 /git-receive-pack
 /git-reflog
+/git-refs
 /git-remote
 /git-remote-http
 /git-remote-https
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c0fa2fe..37b991e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,8 +9,10 @@
 
 test:linux:
   image: $image
+  variables:
+    CUSTOM_PATH: "/custom"
   before_script:
-    - ./ci/install-docker-dependencies.sh
+    - ./ci/install-dependencies.sh
   script:
     - useradd builder --create-home
     - chown -R builder "${CI_PROJECT_DIR}"
@@ -93,12 +95,39 @@
       - t/failed-test-artifacts
     when: on_failure
 
+test:fuzz-smoke-tests:
+  image: ubuntu:latest
+  variables:
+    CC: clang
+  before_script:
+    - ./ci/install-dependencies.sh
+  script:
+    - ./ci/run-build-and-minimal-fuzzers.sh
+
 static-analysis:
   image: ubuntu:22.04
   variables:
     jobname: StaticAnalysis
   before_script:
-    - ./ci/install-docker-dependencies.sh
+    - ./ci/install-dependencies.sh
   script:
     - ./ci/run-static-analysis.sh
     - ./ci/check-directional-formatting.bash
+
+check-whitespace:
+  image: ubuntu:latest
+  before_script:
+    - ./ci/install-dependencies.sh
+  script:
+    - ./ci/check-whitespace.sh "$CI_MERGE_REQUEST_TARGET_BRANCH_SHA"
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+documentation:
+  image: ubuntu:latest
+  variables:
+    jobname: Documentation
+  before_script:
+    - ./ci/install-dependencies.sh
+  script:
+    - ./ci/test-documentation.sh
diff --git a/Documentation/DecisionMaking.txt b/Documentation/DecisionMaking.txt
new file mode 100644
index 0000000..dbb4c1f
--- /dev/null
+++ b/Documentation/DecisionMaking.txt
@@ -0,0 +1,74 @@
+Decision-Making Process in the Git Project
+==========================================
+
+Introduction
+------------
+This document describes the current decision-making process in the Git
+project. It is a descriptive rather than prescriptive doc; that is, we want to
+describe how things work in practice rather than explicitly recommending any
+particular process or changes to the current process.
+
+Here we document how the project makes decisions for discussions
+(with or without patches), in scale larger than an individual patch
+series (which is fully covered by the SubmittingPatches document).
+
+
+Larger Discussions (with patches)
+---------------------------------
+As with discussions on an individual patch series, starting a larger-scale
+discussion often begins by sending a patch or series to the list. This might
+take the form of an initial design doc, with implementation following in later
+iterations of the series (for example,
+link:https://lore.kernel.org/git/0169ce6fb9ccafc089b74ae406db0d1a8ff8ac65.1688165272.git.steadmon@google.com/[adding unit tests] or
+link:https://lore.kernel.org/git/20200420235310.94493-1-emilyshaffer@google.com/[config-based hooks]),
+or it might include a full implementation from the beginning.
+In either case, discussion progresses the same way for an individual patch series,
+until consensus is reached or the topic is dropped.
+
+
+Larger Discussions (without patches)
+------------------------------------
+Occasionally, larger discussions might occur without an associated patch series.
+These may be very large-scale technical decisions that are beyond the scope of
+even a single large patch series, or they may be more open-ended,
+policy-oriented discussions (examples:
+link:https://lore.kernel.org/git/ZZ77NQkSuiRxRDwt@nand.local/[introducing Rust]
+or link:https://lore.kernel.org/git/YHofmWcIAidkvJiD@google.com/[improving submodule UX]).
+In either case, discussion progresses as described above for general patch series.
+
+For larger discussions without a patch series or other concrete implementation,
+it may be hard to judge when consensus has been reached, as there are not any
+official guidelines. If discussion stalls at this point, it may be helpful to
+restart discussion with an RFC patch series (such as a partial, unfinished
+implementation or proof of concept) that can be more easily debated.
+
+When consensus is reached that it is a good idea, the original
+proposer is expected to coordinate the effort to make it happen,
+with help from others who were involved in the discussion, as
+needed.
+
+For decisions that require code changes, it is often the case that the original
+proposer will follow up with a patch series, although it is also common for
+other interested parties to provide an implementation (or parts of the
+implementation, for very large changes).
+
+For non-technical decisions such as community norms or processes, it is up to
+the community as a whole to implement and sustain agreed-upon changes.
+The project leadership committe (PLC) may help the implementation of
+policy decisions.
+
+
+Other Discussion Venues
+-----------------------
+Occasionally decision proposals are presented off-list, e.g. at the semi-regular
+Contributors' Summit. While higher-bandwidth face-to-face discussion is often
+useful for quickly reaching consensus among attendees, generally we expect to
+summarize the discussion in notes that can later be presented on-list. For an
+example, see the thread
+link:https://lore.kernel.org/git/AC2EB721-2979-43FD-922D-C5076A57F24B@jramsay.com.au/[Notes
+from Git Contributor Summit, Los Angeles (April 5, 2020)] by James Ramsay.
+
+We prefer that "official" discussion happens on the list so that the full
+community has opportunity to engage in discussion. This also means that the
+mailing list archives contain a more-or-less complete history of project
+discussions and decisions.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3f2383a..a3868a4 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -103,6 +103,7 @@
 API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technical/api-index.txt, $(wildcard technical/api-*.txt)))
 SP_ARTICLES += $(API_DOCS)
 
+TECH_DOCS += DecisionMaking
 TECH_DOCS += ReviewingGuidelines
 TECH_DOCS += MyFirstContribution
 TECH_DOCS += MyFirstObjectWalk
@@ -484,12 +485,16 @@
 
 lint-docs-fsck-msgids: $(LINT_DOCS_FSCK_MSGIDS)
 
+lint-docs-manpages:
+	$(QUIET_GEN)./lint-manpages.sh
+
 ## Lint: list of targets above
 .PHONY: lint-docs
 lint-docs: lint-docs-fsck-msgids
 lint-docs: lint-docs-gitlink
 lint-docs: lint-docs-man-end-blurb
 lint-docs: lint-docs-man-section-order
+lint-docs: lint-docs-manpages
 
 ifeq ($(wildcard po/Makefile),po/Makefile)
 doc-l10n install-l10n::
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index f06563e..e41654c 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1116,6 +1116,15 @@
 NOTE: Check `git help send-email` for some other options which you may find
 valuable, such as changing the Reply-to address or adding more CC and BCC lines.
 
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+NOTE: If you're not sure whom to CC, running `contrib/contacts/git-contacts` can
+list potential reviewers. In addition, you can do `git send-email
+--cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch`{contrib-scripts} to
+automatically pass this list of emails to `send-email`.
+
 NOTE: When you are sending a real patch, it will go to git@vger.kernel.org - but
 please don't send your patchset from the tutorial to the real mailing list! For
 now, you can send it to yourself, to make sure you understand how it will look.
diff --git a/Documentation/RelNotes/2.39.4.txt b/Documentation/RelNotes/2.39.4.txt
new file mode 100644
index 0000000..7f54521
--- /dev/null
+++ b/Documentation/RelNotes/2.39.4.txt
@@ -0,0 +1,79 @@
+Git v2.39.4 Release Notes
+=========================
+
+This addresses the security issues CVE-2024-32002, CVE-2024-32004,
+CVE-2024-32020 and CVE-2024-32021.
+
+This release also backports fixes necessary to let the CI builds pass
+successfully.
+
+Fixes since v2.39.3
+-------------------
+
+ * CVE-2024-32002:
+
+   Recursive clones on case-insensitive filesystems that support symbolic
+   links are susceptible to case confusion that can be exploited to
+   execute just-cloned code during the clone operation.
+
+ * CVE-2024-32004:
+
+   Repositories can be configured to execute arbitrary code during local
+   clones. To address this, the ownership checks introduced in v2.30.3
+   are now extended to cover cloning local repositories.
+
+ * CVE-2024-32020:
+
+   Local clones may end up hardlinking files into the target repository's
+   object database when source and target repository reside on the same
+   disk. If the source repository is owned by a different user, then
+   those hardlinked files may be rewritten at any point in time by the
+   untrusted user.
+
+ * CVE-2024-32021:
+
+   When cloning a local source repository that contains symlinks via the
+   filesystem, Git may create hardlinks to arbitrary user-readable files
+   on the same filesystem as the target repository in the objects/
+   directory.
+
+ * CVE-2024-32465:
+
+   It is supposed to be safe to clone untrusted repositories, even those
+   unpacked from zip archives or tarballs originating from untrusted
+   sources, but Git can be tricked to run arbitrary code as part of the
+   clone.
+
+ * Defense-in-depth: submodule: require the submodule path to contain
+   directories only.
+
+ * Defense-in-depth: clone: when symbolic links collide with directories, keep
+   the latter.
+
+ * Defense-in-depth: clone: prevent hooks from running during a clone.
+
+ * Defense-in-depth: core.hooksPath: add some protection while cloning.
+
+ * Defense-in-depth: fsck: warn about symlink pointing inside a gitdir.
+
+ * Various fix-ups on HTTP tests.
+
+ * Test update.
+
+ * HTTP Header redaction code has been adjusted for a newer version of
+   cURL library that shows its traces differently from earlier
+   versions.
+
+ * Fix was added to work around a regression in libcURL 8.7.0 (which has
+   already been fixed in their tip of the tree).
+
+ * Replace macos-12 used at GitHub CI with macos-13.
+
+ * ci(linux-asan/linux-ubsan): let's save some time
+
+ * Tests with LSan from time to time seem to emit harmless message that makes
+   our tests unnecessarily flakey; we work it around by filtering the
+   uninteresting output.
+
+ * Update GitHub Actions jobs to avoid warnings against using deprecated
+   version of Node.js.
diff --git a/Documentation/RelNotes/2.39.5.txt b/Documentation/RelNotes/2.39.5.txt
new file mode 100644
index 0000000..97c0185
--- /dev/null
+++ b/Documentation/RelNotes/2.39.5.txt
@@ -0,0 +1,26 @@
+Git v2.39.5 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.39.4 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.40.2.txt b/Documentation/RelNotes/2.40.2.txt
new file mode 100644
index 0000000..646a2cc
--- /dev/null
+++ b/Documentation/RelNotes/2.40.2.txt
@@ -0,0 +1,7 @@
+Git v2.40.2 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4 to address
+the security issues CVE-2024-32002, CVE-2024-32004, CVE-2024-32020,
+CVE-2024-32021 and CVE-2024-32465; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.40.3.txt b/Documentation/RelNotes/2.40.3.txt
new file mode 100644
index 0000000..6ca088e
--- /dev/null
+++ b/Documentation/RelNotes/2.40.3.txt
@@ -0,0 +1,26 @@
+Git v2.40.3 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.40.2 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.41.1.txt b/Documentation/RelNotes/2.41.1.txt
new file mode 100644
index 0000000..9fb4c21
--- /dev/null
+++ b/Documentation/RelNotes/2.41.1.txt
@@ -0,0 +1,7 @@
+Git v2.41.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4 and v2.40.2
+to address the security issues CVE-2024-32002, CVE-2024-32004,
+CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465; see the release
+notes for these versions for details.
diff --git a/Documentation/RelNotes/2.41.2.txt b/Documentation/RelNotes/2.41.2.txt
new file mode 100644
index 0000000..f94afde
--- /dev/null
+++ b/Documentation/RelNotes/2.41.2.txt
@@ -0,0 +1,26 @@
+Git v2.41.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.41.1 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.42.2.txt b/Documentation/RelNotes/2.42.2.txt
new file mode 100644
index 0000000..dbf761a
--- /dev/null
+++ b/Documentation/RelNotes/2.42.2.txt
@@ -0,0 +1,7 @@
+Git v2.42.2 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2
+and v2.41.1 to address the security issues CVE-2024-32002,
+CVE-2024-32004, CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.42.3.txt b/Documentation/RelNotes/2.42.3.txt
new file mode 100644
index 0000000..bfe3ba5
--- /dev/null
+++ b/Documentation/RelNotes/2.42.3.txt
@@ -0,0 +1,26 @@
+Git v2.42.3 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.42.2 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.43.4.txt b/Documentation/RelNotes/2.43.4.txt
new file mode 100644
index 0000000..0a84251
--- /dev/null
+++ b/Documentation/RelNotes/2.43.4.txt
@@ -0,0 +1,7 @@
+Git v2.43.4 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2,
+v2.41.1 and v2.42.2 to address the security issues CVE-2024-32002,
+CVE-2024-32004, CVE-2024-32020, CVE-2024-32021 and CVE-2024-32465;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.43.5.txt b/Documentation/RelNotes/2.43.5.txt
new file mode 100644
index 0000000..236b234
--- /dev/null
+++ b/Documentation/RelNotes/2.43.5.txt
@@ -0,0 +1,26 @@
+Git v2.43.5 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.43.4 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.44.1.txt b/Documentation/RelNotes/2.44.1.txt
new file mode 100644
index 0000000..b5135c3
--- /dev/null
+++ b/Documentation/RelNotes/2.44.1.txt
@@ -0,0 +1,8 @@
+Git v2.44.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4, v2.40.2,
+v2.41.1, v2.42.2 and v2.43.4 to address the security issues
+CVE-2024-32002, CVE-2024-32004, CVE-2024-32020, CVE-2024-32021
+and CVE-2024-32465; see the release notes for these versions
+for details.
diff --git a/Documentation/RelNotes/2.44.2.txt b/Documentation/RelNotes/2.44.2.txt
new file mode 100644
index 0000000..76700f0
--- /dev/null
+++ b/Documentation/RelNotes/2.44.2.txt
@@ -0,0 +1,26 @@
+Git v2.44.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.44.1 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.45.1.txt b/Documentation/RelNotes/2.45.1.txt
new file mode 100644
index 0000000..3b0d60c
--- /dev/null
+++ b/Documentation/RelNotes/2.45.1.txt
@@ -0,0 +1,8 @@
+Git v2.45.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.39.4,
+v2.40.2, v2.41.1, v2.42.2, v2.43.4 and v2.44.1 to address the
+security issues CVE-2024-32002, CVE-2024-32004, CVE-2024-32020,
+CVE-2024-32021 and CVE-2024-32465; see the release notes for
+these versions for details.
diff --git a/Documentation/RelNotes/2.45.2.txt b/Documentation/RelNotes/2.45.2.txt
new file mode 100644
index 0000000..13429e6
--- /dev/null
+++ b/Documentation/RelNotes/2.45.2.txt
@@ -0,0 +1,26 @@
+Git v2.45.2 Release Notes
+=========================
+
+In preparing security fixes for four CVEs, we made overly aggressive
+"defense in depth" changes that broke legitimate use cases like 'git
+lfs' and 'git annex.'  This release is to revert these misguided, if
+well-intentioned, changes that were shipped in 2.45.1 and were not
+direct security fixes.
+
+Jeff King (5):
+      send-email: drop FakeTerm hack
+      send-email: avoid creating more than one Term::ReadLine object
+      ci: drop mention of BREW_INSTALL_PACKAGES variable
+      ci: avoid bare "gcc" for osx-gcc job
+      ci: stop installing "gcc-13" for osx-gcc
+
+Johannes Schindelin (6):
+      hook: plug a new memory leak
+      init: use the correct path of the templates directory again
+      Revert "core.hooksPath: add some protection while cloning"
+      tests: verify that `clone -c core.hooksPath=/dev/null` works again
+      clone: drop the protections where hooks aren't run
+      Revert "Add a helper function to compare file contents"
+
+Junio C Hamano (1):
+      Revert "fsck: warn about symlink pointing inside a gitdir"
diff --git a/Documentation/RelNotes/2.46.0.txt b/Documentation/RelNotes/2.46.0.txt
new file mode 100644
index 0000000..6321ddc
--- /dev/null
+++ b/Documentation/RelNotes/2.46.0.txt
@@ -0,0 +1,248 @@
+Git v2.46 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ (None at this moment)
+
+UI, Workflows & Features
+
+ * The "--rfc" option of "git format-patch" learned to take an
+   optional string value to be used in place of "RFC" to tweak the
+   "[PATCH]" on the subject header.
+
+ * The credential helper protocol, together with the HTTP layer, have
+   been enhanced to support authentication schemes different from
+   username & password pair, like Bearer and NTLM.
+
+ * Command line completion script (in contrib/) learned to complete
+   "git symbolic-ref" a bit better (you need to enable plumbing
+   commands to be completed with GIT_COMPLETION_SHOW_ALL_COMMANDS).
+
+ * When the user responds to a prompt given by "git add -p" with an
+   unsupported command, list of available commands were given, which
+   was too much if the user knew what they wanted to type but merely
+   made a typo.  Now the user gets a much shorter error message.
+
+ * The color parsing code learned to handle 12-bit RGB colors, spelled
+   as "#RGB" (in addition to "#RRGGBB" that is already supported).
+
+ * The operation mode options (like "--get") the "git config" command
+   uses have been deprecated and replaced with subcommands (like "git
+   config get").
+
+ * "git tag" learned the "--trailer" option to futz with the trailers
+   in the same way as "git commit" does.
+
+ * A new global "--no-advice" option can be used to disable all advice
+   messages, which is meant to be used only in scripts.
+
+ * Updates to symbolic refs can now be made as a part of ref
+   transaction.
+
+ * The trailer API has been reshuffled a bit.
+
+ * Terminology to call various ref-like things are getting
+   straightened out.
+
+ * The command line completion script (in contrib/) has been adjusted
+   to the recent update to "git config" that adopted subcommand based
+   UI.
+
+ * The knobs to tweak how reftable files are written have been made
+   available as configuration variables.
+
+ * When "git push" notices that the commit at the tip of the ref on
+   the other side it is about to overwrite does not exist locally, it
+   used to first try fetching it if the local repository is a partial
+   clone. The command has been taught not to do so and immediately
+   fail instead.
+
+ * The promisor.quiet configuration knob can be set to true to make
+   lazy fetching from promisor remotes silent.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Advertise "git contacts", a tool for newcomers to find people to
+   ask review for their patches, a bit more in our developer
+   documentation.
+
+ * In addition to building the objects needed, try to link the objects
+   that are used in fuzzer tests, to make sure at least they build
+   without bitrot, in Linux CI runs.
+
+ * Code to write out reftable has seen some optimization and
+   simplification.
+
+ * Tests to ensure interoperability between reftable written by jgit
+   and our code have been added and enabled in CI.
+
+ * The singleton index_state instance "the_index" has been eliminated
+   by always instantiating "the_repository" and replacing references
+   to "the_index"  with references to its .index member.
+
+ * Git-GUI has a new maintainer, Johannes Sixt.
+
+ * The "test-tool" has been taught to run testsuite tests in parallel,
+   bypassing the need to use the "prove" tool.
+
+ * The "whitespace check" task that was enabled for GitHub Actions CI
+   has been ported to GitLab CI.
+
+ * The refs API lost functions that implicitly assumes to work on the
+   primary ref_store by forcing the callers to pass a ref_store as an
+   argument.
+
+ * Code clean-up to reduce inter-function communication inside
+   builtin/config.c done via the use of global variables.
+
+ * The pack bitmap code saw some clean-up to prepare for a follow-up topic.
+
+ * Preliminary code clean-up for "git send-email".
+
+ * The default "creation-factor" used by "git format-patch" has been
+   raised to make it more aggressively find matching commits.
+
+ * Before discovering the repository details, We used to assume SHA-1
+   as the "default" hash function, which has been corrected. Hopefully
+   this will smoke out codepaths that rely on such an unwarranted
+   assumptions.
+
+ * The project decision making policy has been documented.
+
+ * The strcmp-offset tests have been rewritten using the unit test
+   framework.
+
+ * "git add -p" learned to complain when an answer with more than one
+   letter is given to a prompt that expects a single letter answer.
+
+ * The alias-expanded command lines are logged to the trace output.
+
+ * A new test was added to ensure git commands that are designed to
+   run outside repositories do work.
+
+ * Basic unit tests for reftable have been reimplemented under the
+   unit test framework.
+
+ * A pair of test helpers that essentially are unit tests on hash
+   algorithms have been rewritten using the unit-tests framework.
+
+ * A test helper that essentially is unit tests on the "decorate"
+   logic has been rewritten using the unit-tests framework.
+
+ * Many memory leaks in the sparse-checkout code paths have been
+   plugged.
+
+
+Fixes since v2.45
+-----------------
+
+ * "git rebase --signoff" used to forget that it needs to add a
+   sign-off to the resulting commit when told to continue after a
+   conflict stops its operation.
+   (merge a6c2654f83 pw/rebase-m-signoff-fix later to maint).
+
+ * The procedure to build multi-pack-index got confused by the
+   replace-refs mechanism, which has been corrected by disabling the
+   latter.
+   (merge 93e2ae1c95 xx/disable-replace-when-building-midx later to maint).
+
+ * The "-k" and "--rfc" options of "format-patch" will now error out
+   when used together, as one tells us not to add anything to the
+   title of the commit, and the other one tells us to add "RFC" in
+   addition to "PATCH".
+   (merge cadcf58085 ds/format-patch-rfc-and-k later to maint).
+
+ * "git stash -S" did not handle binary files correctly, which has
+   been corrected.
+   (merge 5fb7686409 aj/stash-staged-fix later to maint).
+
+ * A scheduled "git maintenance" job is expected to work on all
+   repositories it knows about, but it stopped at the first one that
+   errored out.  Now it keeps going.
+   (merge c75662bfc9 js/for-each-repo-keep-going later to maint).
+
+ * zsh can pretend to be a normal shell pretty well except for some
+   glitches that we tickle in some of our scripts. Work them around
+   so that "vimdiff" and our test suite works well enough with it.
+   (merge fedd5c79ff bc/zsh-compatibility later to maint).
+
+ * Command line completion support for zsh (in contrib/) has been
+   updated to stop exposing internal state to end-user shell
+   interaction.
+   (merge 3c20acdf46 dk/zsh-git-repo-path-fix later to maint).
+
+ * Tests that try to corrupt in-repository files in chunked format did
+   not work well on macOS due to its broken "mv", which has been
+   worked around.
+
+ * The maximum size of attribute files is enforced more consistently.
+   (merge c793f9cb08 tb/attr-limits later to maint).
+
+ * Unbreak CI jobs so that we do not attempt to use Python 2 that has
+   been removed from the platform.
+
+ * Git 2.43 started using the tree of HEAD as the source of attributes
+   in a bare repository, which has severe performance implications.
+   For now, revert the change, without ripping out a more explicit
+   support for the attr.tree configuration variable.
+
+ * The "--exit-code" option of "git diff" command learned to work with
+   the "--ext-diff" option.
+   (merge 11be65cfa4 rs/external-diff-with-exit-code later to maint).
+
+ * Windows CI running in GitHub Actions started complaining about the
+   order of arguments given to calloc(); the imported regex code uses
+   the wrong order almost consistently, which has been corrected.
+
+ * Expose "name conflict" error when a ref creation fails due to D/F
+   conflict in the ref namespace, to improve an error message given by
+   "git fetch".
+   (merge 9339fca23e it/refs-name-conflict later to maint).
+
+ * The SubmittingPatches document now refers folks to manpages
+   translation project.
+
+ * The documentation for "git diff --name-only" has been clarified
+   that it is about showing the names in the post-image tree.
+   (merge 4986662cbc jc/doc-diff-name-only later to maint).
+
+ * The credential helper that talks with osx keychain learned to avoid
+   storing back the authentication material it just got received from
+   the keychain.
+   (merge e1ab45b2da kn/osxkeychain-skip-idempotent-store later to maint).
+
+ * The chainlint script (invoked during "make test") did nothing when
+   it failed to detect the number of available CPUs.  It now falls
+   back to 1 CPU to avoid the problem.
+   (merge 2e7e9205be es/chainlint-ncores-fix later to maint).
+
+ * Revert overly aggressive "layered defence" that went into 2.45.1
+   and friends, which broke "git-lfs", "git-annex", and other use
+   cases, so that we can rebuild necessary counterparts in the open.
+
+ * "git init" in an already created directory, when the user
+   configuration has includeif.onbranch, started to fail recently,
+   which has been corrected.
+   (merge 407997c1dd ps/fix-reinit-includeif-onbranch later to maint).
+
+ * Memory leaks in "git mv" has been plugged.
+
+ * The safe.directory configuration knob has been updated to
+   optionally allow leading path matches.
+   (merge 313eec177a jc/safe-directory-leading-path later to maint).
+
+ * An overly large ".gitignore" files are now rejected silently.
+   (merge e7c3d1ddba jk/cap-exclude-file-size later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge a5a4cb7b27 rs/diff-parseopts-cleanup later to maint).
+   (merge 55702c543e fa/p4-error later to maint).
+   (merge 2566a77774 vd/doc-merge-tree-x-option later to maint).
+   (merge b64b0df9da ds/scalar-reconfigure-all-fix later to maint).
+   (merge c81ffcff83 dm/update-index-doc-fix later to maint).
+   (merge fc0202b0e9 dg/fetch-pack-code-cleanup later to maint).
+   (merge 7150f140f9 mt/t0211-typofix later to maint).
+   (merge d424488901 jc/rev-parse-fatal-doc later to maint).
+   (merge 36d900d2b0 rs/difftool-env-simplify later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index c647c7e..d8a8caa 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -7,6 +7,73 @@
 project. There is also a link:MyFirstContribution.html[step-by-step tutorial]
 available which covers many of these same guidelines.
 
+[[patch-flow]]
+=== A typical life cycle of a patch series
+
+To help us understand the reason behind various guidelines given later
+in the document, first let's understand how the life cycle of a
+typical patch series for this project goes.
+
+. You come up with an itch.  You code it up.  You do not need any
+  pre-authorization from the project to do so.
++
+Your patches will be reviewed by other contributors on the mailing
+list, and the reviews will be done to assess the merit of various
+things, like the general idea behind your patch (including "is it
+solving a problem worth solving in the first place?"), the reason
+behind the design of the solution, and the actual implementation.
+The guidelines given here are there to help your patches by making
+them easier to understand by the reviewers.
+
+. You send the patches to the list and cc people who may need to know
+  about the change.  Your goal is *not* necessarily to convince others
+  that what you are building is good.  Your goal is to get help in
+  coming up with a solution for the "itch" that is better than what
+  you can build alone.
++
+The people who may need to know are the ones who worked on the code
+you are touching.  These people happen to be the ones who are
+most likely to be knowledgeable enough to help you, but
+they have no obligation to help you (i.e. you ask them for help,
+you don't demand).  +git log -p {litdd} _$area_you_are_modifying_+ would
+help you find out who they are.
+
+. You get comments and suggestions for improvements.  You may even get
+  them in an "on top of your change" patch form.  You are expected to
+  respond to them with "Reply-All" on the mailing list, while taking
+  them into account while preparing an updated set of patches.
+
+. Polish, refine, and re-send your patches to the list and to the people
+  who spent their time to improve your patch.  Go back to step (2).
+
+. While the above iterations improve your patches, the maintainer may
+  pick the patches up from the list and queue them to the `seen`
+  branch, in order to make it easier for people to play with it
+  without having to pick up and apply the patches to their trees
+  themselves.  Being in `seen` has no other meaning.  Specifically, it
+  does not mean the patch was "accepted" in any way.
+
+. When the discussion reaches a consensus that the latest iteration of
+  the patches are in good enough shape, the maintainer includes the
+  topic in the "What's cooking" report that are sent out a few times a
+  week to the mailing list, marked as "Will merge to 'next'."  This
+  decision is primarily made by the maintainer with help from those
+  who participated in the review discussion.
+
+. After the patches are merged to the 'next' branch, the discussion
+  can still continue to further improve them by adding more patches on
+  top, but by the time a topic gets merged to 'next', it is expected
+  that everybody agrees that the scope and the basic direction of the
+  topic are appropriate, so such an incremental updates are limited to
+  small corrections and polishing.  After a topic cooks for some time
+  (like 7 calendar days) in 'next' without needing further tweaks on
+  top, it gets merged to the 'master' branch and wait to become part
+  of the next major release.
+
+In the following sections, many techniques and conventions are listed
+to help your patches get reviewed effectively in such a life cycle.
+
+
 [[choose-starting-point]]
 === Choose a starting point.
 
@@ -192,8 +259,9 @@
   which case, they can explain why they extend your code to cover
   files, too).
 
-The goal of your log message is to convey the _why_ behind your
-change to help future developers.
+The goal of your log message is to convey the _why_ behind your change
+to help future developers.  The reviewers will also make sure that
+your proposed log message will serve this purpose well.
 
 The first line of the commit message should be a short description (50
 characters is the soft limit, see DISCUSSION in linkgit:git-commit[1]),
@@ -397,17 +465,57 @@
 [[send-patches]]
 === Sending your patches.
 
+==== Choosing your reviewers
+
 :security-ml: footnoteref:[security-ml,The Git Security mailing list: git-security@googlegroups.com]
 
-Before sending any patches, please note that patches that may be
+NOTE: Patches that may be
 security relevant should be submitted privately to the Git Security
 mailing list{security-ml}, instead of the public mailing list.
 
-Learn to use format-patch and send-email if possible.  These commands
+:contrib-scripts: footnoteref:[contrib-scripts,Scripts under `contrib/` are +
+not part of the core `git` binary and must be called directly. Clone the Git +
+codebase and run `perl contrib/contacts/git-contacts`.]
+
+Send your patch with "To:" set to the mailing list, with "cc:" listing
+people who are involved in the area you are touching (the `git-contacts`
+script in `contrib/contacts/`{contrib-scripts} can help to
+identify them), to solicit comments and reviews.  Also, when you made
+trial merges of your topic to `next` and `seen`, you may have noticed
+work by others conflicting with your changes.  There is a good possibility
+that these people may know the area you are touching well.
+
+If you are using `send-email`, you can feed it the output of `git-contacts` like
+this:
+
+....
+	git send-email --cc-cmd='perl contrib/contacts/git-contacts' feature/*.patch
+....
+
+:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
+:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+
+After the list reached a consensus that it is a good idea to apply the
+patch, re-send it with "To:" set to the maintainer{current-maintainer}
+and "cc:" the list{git-ml} for inclusion.  This is especially relevant
+when the maintainer did not heavily participate in the discussion and
+instead left the review to trusted others.
+
+Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
+`Tested-by:` lines as necessary to credit people who helped your
+patch, and "cc:" them when sending such a final version for inclusion.
+
+==== `format-patch` and `send-email`
+
+Learn to use `format-patch` and `send-email` if possible.  These commands
 are optimized for the workflow of sending patches, avoiding many ways
 your existing e-mail client (often optimized for "multipart/*" MIME
 type e-mails) might render your patches unusable.
 
+NOTE: Here we outline the procedure using `format-patch` and
+`send-email`, but you can instead use GitGitGadget to send in your
+patches (see link:MyFirstContribution.html[MyFirstContribution]).
+
 People on the Git mailing list need to be able to read and
 comment on the changes you are submitting.  It is important for
 a developer to be able to "quote" your changes, using standard
@@ -415,10 +523,12 @@
 your code.  For this reason, each patch should be submitted
 "inline" in a separate message.
 
-Multiple related patches should be grouped into their own e-mail
-thread to help readers find all parts of the series.  To that end,
-send them as replies to either an additional "cover letter" message
-(see below), the first patch, or the respective preceding patch.
+All subsequent versions of a patch series and other related patches should be
+grouped into their own e-mail thread to help readers find all parts of the
+series.  To that end, send them as replies to either an additional "cover
+letter" message (see below), the first patch, or the respective preceding patch.
+Here is a link:MyFirstContribution.html#v2-git-send-email[step-by-step guide] on
+how to submit updated versions of a patch series.
 
 If your log message (including your name on the
 `Signed-off-by` trailer) is not writable in ASCII, make sure that
@@ -498,42 +608,93 @@
 that starts with `-----BEGIN PGP SIGNED MESSAGE-----`.  That is
 not a text/plain, it's something else.
 
-:security-ml-ref: footnoteref:[security-ml]
+=== Handling Conflicts and Iterating Patches
 
-As mentioned at the beginning of the section, patches that may be
-security relevant should not be submitted to the public mailing list
-mentioned below, but should instead be sent privately to the Git
-Security mailing list{security-ml-ref}.
+When revising changes made to your patches, it's important to
+acknowledge the possibility of conflicts with other ongoing topics. To
+navigate these potential conflicts effectively, follow the recommended
+steps outlined below:
 
-Send your patch with "To:" set to the mailing list, with "cc:" listing
-people who are involved in the area you are touching (the `git
-contacts` command in `contrib/contacts/` can help to
-identify them), to solicit comments and reviews.  Also, when you made
-trial merges of your topic to `next` and `seen`, you may have noticed
-work by others conflicting with your changes.  There is a good possibility
-that these people may know the area you are touching well.
+. Build on a suitable base branch, see the <<choose-starting-point, section above>>,
+and format-patch the series. If you are doing "rebase -i" in-place to
+update from the previous round, this will reuse the previous base so
+(2) and (3) may become trivial.
 
-:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
-:git-ml: footnote:[The mailing list: git@vger.kernel.org]
+. Find the base of where the last round was queued
++
+    $ mine='kn/ref-transaction-symref'
+    $ git checkout "origin/seen^{/^Merge branch '$mine'}...master"
 
-After the list reached a consensus that it is a good idea to apply the
-patch, re-send it with "To:" set to the maintainer{current-maintainer}
-and "cc:" the list{git-ml} for inclusion.  This is especially relevant
-when the maintainer did not heavily participate in the discussion and
-instead left the review to trusted others.
+. Apply your format-patch result.  There are two cases
+.. Things apply cleanly and tests fine.  Go to (4).
+.. Things apply cleanly but does not build or test fails, or things do
+not apply cleanly.
++
+In the latter case, you have textual or semantic conflicts coming from
+the difference between the old base and the base you used to build in
+(1).  Identify what caused the breakages (e.g., a topic or two may have
+merged since the base used by (2) until the base used by (1)).
++
+Check out the latest 'origin/master' (which may be newer than the base
+used by (2)), "merge --no-ff" the topics you newly depend on in there,
+and use the result of the merge(s) as the base, rebuild the series and
+test again.  Run format-patch from the last such merges to the tip of
+your topic.  If you did
++
+    $ git checkout origin/master
+    $ git merge --no-ff --into-name kn/ref-transaction-symref fo/obar
+    $ git merge --no-ff --into-name kn/ref-transaction-symref ba/zqux
+    ... rebuild the topic ...
++
+Then you'd just format your topic above these "preparing the ground"
+merges, e.g.
++
+    $ git format-patch "HEAD^{/^Merge branch 'ba/zqux'}"..HEAD
++
+Do not forget to write in the cover letter you did this, including the
+topics you have in your base on top of 'master'.  Then go to (4).
 
-Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
-`Tested-by:` lines as necessary to credit people who helped your
-patch, and "cc:" them when sending such a final version for inclusion.
+. Make a trial merge of your topic into 'next' and 'seen', e.g.
++
+    $ git checkout --detach 'origin/seen'
+    $ git revert -m 1 <the merge of the previous iteration into seen>
+    $ git merge kn/ref-transaction-symref
++
+The "revert" is needed if the previous iteration of your topic is
+already in 'seen' (like in this case).  You could choose to rebuild
+master..origin/seen from scratch while excluding your previous
+iteration, which may emulate what happens on the maintainers end more
+closely.
++
+This trial merge may conflict.  It is primarily to see what conflicts
+_other_ topics may have with your topic.  In other words, you do not
+have to depend on it to make your topic work on 'master'.  It may
+become the job of the other topic owners to resolve conflicts if your
+topic goes to 'next' before theirs.
++
+Make a note on what conflict you saw in the cover letter.  You do not
+necessarily have to resolve them, but it would be a good opportunity to
+learn what others are doing in related areas.
++
+    $ git checkout --detach 'origin/next'
+    $ git merge kn/ref-transaction-symref
++
+This is to see what conflicts your topic has with other topics that are
+already cooking.  This should not conflict if (3)-2 prepared a base on
+top of updated master plus dependent topics taken from 'next'.  Unless
+the context is severe (one way to tell is try the same trial merge with
+your old iteration, which may conflict in a similar way), expect that it
+will be handled on maintainers end (if it gets unmanageable, I'll ask to
+rebase when I receive your patches).
 
 == Subsystems with dedicated maintainers
 
 Some parts of the system have dedicated maintainers with their own
 repositories.
 
-- `git-gui/` comes from git-gui project, maintained by Pratyush Yadav:
+- `git-gui/` comes from git-gui project, maintained by Johannes Sixt:
 
-	https://github.com/prati0100/git-gui.git
+        https://github.com/j6t/git-gui
 
 - `gitk-git/` comes from Paul Mackerras's gitk project:
 
@@ -548,54 +709,12 @@
 
 Patches to these parts should be based on their trees.
 
-[[patch-flow]]
-== An ideal patch flow
+- The "Git documentation translations" project, led by Jean-Noël
+  Avila, translates our documentation pages.  Their work products are
+  maintained separately from this project, not as part of our tree:
 
-Here is an ideal patch flow for this project the current maintainer
-suggests to the contributors:
+	https://github.com/jnavila/git-manpages-l10n/
 
-. You come up with an itch.  You code it up.
-
-. Send it to the list and cc people who may need to know about
-  the change.
-+
-The people who may need to know are the ones whose code you
-are butchering.  These people happen to be the ones who are
-most likely to be knowledgeable enough to help you, but
-they have no obligation to help you (i.e. you ask for help,
-don't demand).  +git log -p {litdd} _$area_you_are_modifying_+ would
-help you find out who they are.
-
-. You get comments and suggestions for improvements.  You may
-  even get them in an "on top of your change" patch form.
-
-. Polish, refine, and re-send to the list and the people who
-  spend their time to improve your patch.  Go back to step (2).
-
-. The list forms consensus that the last round of your patch is
-  good.  Send it to the maintainer and cc the list.
-
-. A topic branch is created with the patch and is merged to `next`,
-  and cooked further and eventually graduates to `master`.
-
-In any time between the (2)-(3) cycle, the maintainer may pick it up
-from the list and queue it to `seen`, in order to make it easier for
-people to play with it without having to pick up and apply the patch to
-their trees themselves.
-
-[[patch-status]]
-== Know the status of your patch after submission
-
-* You can use Git itself to find out when your patch is merged in
-  master. `git pull --rebase` will automatically skip already-applied
-  patches, and will let you know. This works only if you rebase on top
-  of the branch in which your patch has been merged (i.e. it will not
-  tell you if your patch is merged in `seen` if you rebase on top of
-  master).
-
-* Read the Git mailing list, the maintainer regularly posts messages
-  entitled "What's cooking in git.git" giving
-  the status of various proposed changes.
 
 == GitHub CI[[GHCI]]
 
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 70b448b..2d2d06d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -316,7 +316,8 @@
 Colors may also be given as numbers between 0 and 255; these use ANSI
 256-color mode (but note that not all terminals may support this).  If
 your terminal supports it, you may also specify 24-bit RGB values as
-hex, like `#ff0ab3`.
+hex, like `#ff0ab3`, or 12-bit RGB values like `#f1b`, which is
+equivalent to the 24-bit color `#ff11bb`.
 +
 The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
 `italic`, and `strike` (for crossed-out or "strikethrough" letters).
@@ -487,6 +488,8 @@
 
 include::config/pretty.txt[]
 
+include::config/promisor.txt[]
+
 include::config/protocol.txt[]
 
 include::config/pull.txt[]
@@ -497,6 +500,8 @@
 
 include::config/receive.txt[]
 
+include::config/reftable.txt[]
+
 include::config/remote.txt[]
 
 include::config/remotes.txt[]
diff --git a/Documentation/config/alias.txt b/Documentation/config/alias.txt
index 01df96f..2c5db0a 100644
--- a/Documentation/config/alias.txt
+++ b/Documentation/config/alias.txt
@@ -21,8 +21,23 @@
 it will be treated as a shell command.  For example, defining
 `alias.new = !gitk --all --not ORIG_HEAD`, the invocation
 `git new` is equivalent to running the shell command
-`gitk --all --not ORIG_HEAD`.  Note that shell commands will be
-executed from the top-level directory of a repository, which may
-not necessarily be the current directory.
-`GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
-from the original current directory. See linkgit:git-rev-parse[1].
+`gitk --all --not ORIG_HEAD`.  Note:
++
+* Shell commands will be executed from the top-level directory of a
+  repository, which may not necessarily be the current directory.
+* `GIT_PREFIX` is set as returned by running `git rev-parse --show-prefix`
+  from the original current directory. See linkgit:git-rev-parse[1].
+* Shell command aliases always receive any extra arguments provided to
+  the Git command-line as positional arguments.
+** Care should be taken if your shell alias is a "one-liner" script
+   with multiple commands (e.g. in a pipeline), references multiple
+   arguments, or is otherwise not able to handle positional arguments
+   added at the end.  For example: `alias.cmd = "!echo $1 | grep $2"`
+   called as `git cmd 1 2` will be executed as 'echo $1 | grep $2
+   1 2', which is not what you want.
+** A convenient way to deal with this is to write your script
+   operations in an inline function that is then called with any
+   arguments from the command-line.  For example `alias.cmd = "!c() {
+   echo $1 | grep $2 ; }; c" will correctly execute the prior example.
+** Setting `GIT_TRACE=1` can help you debug the command being run for
+   your alias.
diff --git a/Documentation/config/promisor.txt b/Documentation/config/promisor.txt
new file mode 100644
index 0000000..98c5cb2
--- /dev/null
+++ b/Documentation/config/promisor.txt
@@ -0,0 +1,3 @@
+promisor.quiet::
+	If set to "true" assume `--quiet` when fetching additional
+	objects for a partial clone.
diff --git a/Documentation/config/reftable.txt b/Documentation/config/reftable.txt
new file mode 100644
index 0000000..0515727
--- /dev/null
+++ b/Documentation/config/reftable.txt
@@ -0,0 +1,48 @@
+reftable.blockSize::
+	The size in bytes used by the reftable backend when writing blocks.
+	The block size is determined by the writer, and does not have to be a
+	power of 2. The block size must be larger than the longest reference
+	name or log entry used in the repository, as references cannot span
+	blocks.
++
+Powers of two that are friendly to the virtual memory system or
+filesystem (such as 4kB or 8kB) are recommended. Larger sizes (64kB) can
+yield better compression, with a possible increased cost incurred by
+readers during access.
++
+The largest block size is `16777215` bytes (15.99 MiB). The default value is
+`4096` bytes (4kB). A value of `0` will use the default value.
+
+reftable.restartInterval::
+	The interval at which to create restart points. The reftable backend
+	determines the restart points at file creation. Every 16 may be
+	more suitable for smaller block sizes (4k or 8k), every 64 for larger
+	block sizes (64k).
++
+More frequent restart points reduces prefix compression and increases
+space consumed by the restart table, both of which increase file size.
++
+Less frequent restart points makes prefix compression more effective,
+decreasing overall file size, with increased penalties for readers
+walking through more records after the binary search step.
++
+A maximum of `65535` restart points per block is supported.
++
+The default value is to create restart points every 16 records. A value of `0`
+will use the default value.
+
+reftable.indexObjects::
+	Whether the reftable backend shall write object blocks. Object blocks
+	are a reverse mapping of object ID to the references pointing to them.
++
+The default value is `true`.
+
+reftable.geometricFactor::
+	Whenever the reftable backend appends a new table to the stack, it
+	performs auto compaction to ensure that there is only a handful of
+	tables. The backend does this by ensuring that tables form a geometric
+	sequence regarding the respective sizes of each table.
++
+By default, the geometric sequence uses a factor of 2, meaning that for any
+table, the next-biggest table must at least be twice as big. A maximum factor
+of 256 is supported.
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 577df40..2d45c98 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -44,7 +44,8 @@
 directory was listed in the `safe.directory` list. If `safe.directory=*`
 is set in system config and you want to re-enable this protection, then
 initialize your list with an empty value before listing the repositories
-that you deem safe.
+that you deem safe.  Giving a directory with `/*` appended to it will
+allow access to all repositories under the named directory.
 +
 As explained, Git only allows you to access repositories owned by
 yourself, i.e. the user who is running Git, by default.  When Git
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 0e94569..c7df20e 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -329,12 +329,13 @@
 linkgit:git-config[1]).
 
 --name-only::
-	Show only names of changed files. The file names are often encoded in UTF-8.
+	Show only the name of each changed file in the post-image tree.
+	The file names are often encoded in UTF-8.
 	For more information see the discussion about encoding in the linkgit:git-log[1]
 	manual page.
 
 --name-status::
-	Show only names and status of changed files. See the description
+	Show only the name(s) and status of each changed file. See the description
 	of the `--diff-filter` option on what the status letters mean.
 	Just like `--name-only` the file names are often encoded in UTF-8.
 
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index ac61113..65c645d 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,21 +9,14 @@
 SYNOPSIS
 --------
 [verse]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] --add <name> <value>
-'git config' [<file-option>] [--type=<type>] [--comment=<message>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
-'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
-'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
-'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
-'git config' [<file-option>] --rename-section <old-name> <new-name>
-'git config' [<file-option>] --remove-section <name>
-'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
-'git config' [<file-option>] --get-color <name> [<default>]
+'git config list' [<file-option>] [<display-option>] [--includes]
+'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>
+'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>
+'git config rename-section' [<file-option>] <old-name> <new-name>
+'git config remove-section' [<file-option>] <name>
+'git config edit' [<file-option>]
 'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
-'git config' [<file-option>] -e | --edit
 
 DESCRIPTION
 -----------
@@ -31,7 +24,7 @@
 actually the section and the key separated by a dot, and the value will be
 escaped.
 
-Multiple lines can be added to an option by using the `--add` option.
+Multiple lines can be added to an option by using the `--append` option.
 If you want to update or unset an option which can occur on multiple
 lines, a `value-pattern` (which is an extended regular expression,
 unless the `--fixed-value` option is given) needs to be given.  Only the
@@ -74,6 +67,42 @@
 A list of all available configuration variables can be obtained using the
 `git help --config` command.
 
+COMMANDS
+--------
+
+list::
+	List all variables set in config file, along with their values.
+
+get::
+	Emits the value of the specified key. If key is present multiple times
+	in the configuration, emits the last value. If `--all` is specified,
+	emits all values associated with key. Returns error code 1 if key is
+	not present.
+
+set::
+	Set value for one or more config options. By default, this command
+	refuses to write multi-valued config options. Passing `--all` will
+	replace all multi-valued config options with the new value, whereas
+	`--value=` will replace all config options whose values match the given
+	pattern.
+
+unset::
+	Unset value for one or more config options. By default, this command
+	refuses to unset multi-valued keys. Passing `--all` will unset all
+	multi-valued config options, whereas `--value` will unset all config
+	options whose values match the given pattern.
+
+rename-section::
+	Rename the given section to a new name.
+
+remove-section::
+	Remove the given section from the configuration file.
+
+edit::
+	Opens an editor to modify the specified config file; either
+	`--system`, `--global`, `--local` (default), `--worktree`, or
+	`--file <config-file>`.
+
 [[OPTIONS]]
 OPTIONS
 -------
@@ -82,10 +111,9 @@
 	Default behavior is to replace at most one line. This replaces
 	all lines matching the key (and optionally the `value-pattern`).
 
---add::
+--append::
 	Adds a new line to the option without altering any existing
-	values.  This is the same as providing '^$' as the `value-pattern`
-	in `--replace-all`.
+	values. This is the same as providing '--value=^$' in `set`.
 
 --comment <message>::
 	Append a comment at the end of new or modified lines.
@@ -99,22 +127,16 @@
 	not contain linefeed characters (no multi-line comments are
 	permitted).
 
---get::
-	Get the value for a given key (optionally filtered by a regex
-	matching the value). Returns error code 1 if the key was not
-	found and the last value if multiple key values were found.
+--all::
+	With `get`, return all values for a multi-valued key.
 
---get-all::
-	Like get, but returns all values for a multi-valued key.
+---regexp::
+	With `get`, interpret the name as a regular expression. Regular
+	expression matching is currently case-sensitive and done against a
+	canonicalized version of the key in which section and variable names
+	are lowercased, but subsection names are not.
 
---get-regexp::
-	Like --get-all, but interprets the name as a regular expression and
-	writes out the key names.  Regular expression matching is currently
-	case-sensitive and done against a canonicalized version of the key
-	in which section and variable names are lowercased, but subsection
-	names are not.
-
---get-urlmatch <name> <URL>::
+--url=<URL>::
 	When given a two-part <name> as <section>.<key>, the value for
 	<section>.<URL>.<key> whose <URL> part matches the best to the
 	given URL is returned (if no such key exists, the value for
@@ -178,22 +200,6 @@
 	section in linkgit:gitrevisions[7] for a more complete list of
 	ways to spell blob names.
 
---remove-section::
-	Remove the given section from the configuration file.
-
---rename-section::
-	Rename the given section to a new name.
-
---unset::
-	Remove the line matching the key from config file.
-
---unset-all::
-	Remove all lines matching the key from config file.
-
--l::
---list::
-	List all variables set in config file, along with their values.
-
 --fixed-value::
 	When used with the `value-pattern` argument, treat `value-pattern` as
 	an exact string instead of a regular expression. This will restrict
@@ -248,8 +254,8 @@
 	contain line breaks.
 
 --name-only::
-	Output only the names of config variables for `--list` or
-	`--get-regexp`.
+	Output only the names of config variables for `list` or
+	`get`.
 
 --show-origin::
 	Augment the output of all queried config options with the
@@ -273,23 +279,6 @@
 	When the color setting for `name` is undefined, the command uses
 	`color.ui` as fallback.
 
---get-color <name> [<default>]::
-
-	Find the color configured for `name` (e.g. `color.diff.new`) and
-	output it as the ANSI color escape sequence to the standard
-	output.  The optional `default` parameter is used instead, if
-	there is no color configured for `name`.
-+
-`--type=color [--default=<default>]` is preferred over `--get-color`
-(but note that `--get-color` will omit the trailing newline printed by
-`--type=color`).
-
--e::
---edit::
-	Opens an editor to modify the specified config file; either
-	`--system`, `--global`, `--local` (default), `--worktree`, or
-	`--file <config-file>`.
-
 --[no-]includes::
 	Respect `include.*` directives in config files when looking up
 	values. Defaults to `off` when a specific file is given (e.g.,
@@ -297,14 +286,64 @@
 	config files.
 
 --default <value>::
-  When using `--get`, and the requested variable is not found, behave as if
+  When using `get`, and the requested variable is not found, behave as if
   <value> were the value assigned to that variable.
 
+DEPRECATED MODES
+----------------
+
+The following modes have been deprecated in favor of subcommands. It is
+recommended to migrate to the new syntax.
+
+'git config <name>'::
+	Replaced by `git config get <name>`.
+
+'git config <name> <value> [<value-pattern>]'::
+	Replaced by `git config set [--value=<pattern>] <name> <value>`.
+
+-l::
+--list::
+	Replaced by `git config list`.
+
+--get <name> [<value-pattern>]::
+	Replaced by `git config get [--value=<pattern>] <name>`.
+
+--get-all <name> [<value-pattern>]::
+	Replaced by `git config get [--value=<pattern>] --all --show-names <name>`.
+
+--get-regexp <name-regexp>::
+	Replaced by `git config get --all --show-names --regexp <name-regexp>`.
+
+--get-urlmatch <name> <URL>::
+	Replaced by `git config get --all --show-names --url=<URL> <name>`.
+
+--get-color <name> [<default>]::
+	Replaced by `git config get --type=color [--default=<default>] <name>`.
+
+--add <name> <value>::
+	Replaced by `git config set --append <name> <value>`.
+
+--unset <name> [<value-pattern>]::
+	Replaced by `git config unset [--value=<pattern>] <name>`.
+
+--unset-all <name> [<value-pattern>]::
+	Replaced by `git config unset [--value=<pattern>] --all <name>`.
+
+--rename-section <old-name> <new-name>::
+	Replaced by `git config rename-section <old-name> <new-name>`.
+
+--remove-section <name>::
+	Replaced by `git config remove-section <name>`.
+
+-e::
+--edit::
+	Replaced by `git config edit`.
+
 CONFIGURATION
 -------------
 `pager.config` is only respected when listing configuration, i.e., when
-using `--list` or any of the `--get-*` which may return multiple results.
-The default is to use a pager.
+using `list` or `get` which may return multiple results. The default is to use
+a pager.
 
 [[FILES]]
 FILES
@@ -346,8 +385,8 @@
 values of a key from all files will be used.
 
 By default, options are only written to the repository specific
-configuration file. Note that this also affects options like `--replace-all`
-and `--unset`. *'git config' will only ever change one file at a time*.
+configuration file. Note that this also affects options like `set`
+and `unset`. *'git config' will only ever change one file at a time*.
 
 You can limit which configuration sources are read from or written to by
 specifying the path of a file with the `--file` option, or by specifying a
@@ -482,7 +521,7 @@
 you can set the filemode to true with
 
 ------------
-% git config core.filemode true
+% git config set core.filemode true
 ------------
 
 The hypothetical proxy command entries actually have a postfix to discern
@@ -490,7 +529,7 @@
 to "ssh".
 
 ------------
-% git config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$'
+% git config set --value='for kernel.org$' core.gitproxy '"ssh" for kernel.org'
 ------------
 
 This makes sure that only the key/value pair for kernel.org is replaced.
@@ -498,7 +537,7 @@
 To delete the entry for renames, do
 
 ------------
-% git config --unset diff.renames
+% git config unset diff.renames
 ------------
 
 If you want to delete an entry for a multivar (like core.gitproxy above),
@@ -507,51 +546,45 @@
 To query the value for a given key, do
 
 ------------
-% git config --get core.filemode
-------------
-
-or
-
-------------
-% git config core.filemode
+% git config get core.filemode
 ------------
 
 or, to query a multivar:
 
 ------------
-% git config --get core.gitproxy "for kernel.org$"
+% git config get --value="for kernel.org$" core.gitproxy
 ------------
 
 If you want to know all the values for a multivar, do:
 
 ------------
-% git config --get-all core.gitproxy
+% git config get --all --show-names core.gitproxy
 ------------
 
 If you like to live dangerously, you can replace *all* core.gitproxy by a
 new one with
 
 ------------
-% git config --replace-all core.gitproxy ssh
+% git config set --all core.gitproxy ssh
 ------------
 
 However, if you really only want to replace the line for the default proxy,
 i.e. the one without a "for ..." postfix, do something like this:
 
 ------------
-% git config core.gitproxy ssh '! for '
+% git config set --value='! for ' core.gitproxy ssh
 ------------
 
 To actually match only values with an exclamation mark, you have to
 
 ------------
-% git config section.key value '[!]'
+% git config set --value='[!]' section.key value
 ------------
 
 To add a new proxy, without altering any of the existing ones, use
 
 ------------
-% git config --add core.gitproxy '"proxy-command" for example.com'
+% git config set --append core.gitproxy '"proxy-command" for example.com'
 ------------
 
 An example to use customized color from the configuration in your
@@ -559,8 +592,8 @@
 
 ------------
 #!/bin/sh
-WS=$(git config --get-color color.diff.whitespace "blue reverse")
-RESET=$(git config --get-color "" "reset")
+WS=$(git config get --type=color --default="blue reverse" color.diff.whitespace)
+RESET=$(git config get --type=color --default="reset" "")
 echo "${WS}your whitespace color or blue reverse${RESET}"
 ------------
 
@@ -568,11 +601,11 @@
 false, while it is set to `true` for all others:
 
 ------------
-% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
+% git config get --type=bool --url=https://good.example.com http.sslverify
 true
-% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
+% git config get --type=bool --url=https://weak.example.com http.sslverify
 false
-% git config --get-urlmatch http https://weak.example.com
+% git config get --url=https://weak.example.com http
 http.cookieFile /tmp/cookie.txt
 http.sslverify false
 ------------
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 918a0aa..e414932 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 ------------------
-'git credential' (fill|approve|reject)
+'git credential' (fill|approve|reject|capability)
 ------------------
 
 DESCRIPTION
@@ -41,6 +41,9 @@
 any configured credential helpers, which may erase any stored
 credentials matching the description.
 
+If the action is `capability`, git-credential will announce any capabilities
+it supports to standard output.
+
 If the action is `approve` or `reject`, no output should be emitted.
 
 TYPICAL USE OF GIT CREDENTIAL
@@ -111,7 +114,9 @@
 separated by an `=` (equals) sign, followed by a newline.
 
 The key may contain any bytes except `=`, newline, or NUL. The value may
-contain any bytes except newline or NUL.
+contain any bytes except newline or NUL.  A line, including the trailing
+newline, may not exceed 65535 bytes in order to allow implementations to
+parse efficiently.
 
 Attributes with keys that end with C-style array brackets `[]` can have
 multiple values. Each instance of a multi-valued attribute forms an
@@ -178,6 +183,61 @@
 Components which are missing from the URL (e.g., there is no
 username in the example above) will be left unset.
 
+`authtype`::
+	This indicates that the authentication scheme in question should be used.
+	Common values for HTTP and HTTPS include `basic`, `bearer`, and `digest`,
+	although the latter is insecure and should not be used.  If `credential`
+	is used, this may be set to an arbitrary string suitable for the protocol in
+	question (usually HTTP).
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`credential`::
+	The pre-encoded credential, suitable for the protocol in question (usually
+	HTTP).  If this key is sent, `authtype` is mandatory, and `username` and
+	`password` are not used.  For HTTP, Git concatenates the `authtype` value and
+	this value with a single space to determine the `Authorization` header.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`ephemeral`::
+	This boolean value indicates, if true, that the value in the `credential`
+	field should not be saved by the credential helper because its usefulness is
+	limited in time.  For example, an HTTP Digest `credential` value is computed
+	using a nonce and reusing it will not result in successful authentication.
+	This may also be used for situations with short duration (e.g., 24-hour)
+	credentials.  The default value is false.
++
+The credential helper will still be invoked with `store` or `erase` so that it
+can determine whether the operation was successful.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`state[]`::
+	This value provides an opaque state that will be passed back to this helper
+	if it is called again.  Each different credential helper may specify this
+	once.  The value should include a prefix unique to the credential helper and
+	should ignore values that don't match its prefix.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.
+
+`continue`::
+	This is a boolean value, which, if enabled, indicates that this
+	authentication is a non-final part of a multistage authentication step. This
+	is common in protocols such as NTLM and Kerberos, where two rounds of client
+	authentication are required, and setting this flag allows the credential
+	helper to implement the multistage authentication step.  This flag should
+	only be sent if a further stage is required; that is, if another round of
+	authentication is expected.
++
+This value should not be sent unless the appropriate capability (see below) is
+provided on input.  This attribute is 'one-way' from a credential helper to
+pass information to Git (or other programs invoking `git credential`).
+
 `wwwauth[]`::
 
 	When an HTTP response is received by Git that includes one or more
@@ -189,7 +249,45 @@
 they appear in the HTTP response. This attribute is 'one-way' from Git
 to pass additional information to credential helpers.
 
-Unrecognised attributes are silently discarded.
+`capability[]`::
+	This signals that Git, or the helper, as appropriate, supports the capability
+	in question.  This can be used to provide better, more specific data as part
+	of the protocol.  A `capability[]` directive must precede any value depending
+	on it and these directives _should_ be the first item announced in the
+	protocol.
++
+There are two currently supported capabilities.  The first is `authtype`, which
+indicates that the `authtype`, `credential`, and `ephemeral` values are
+understood.  The second is `state`, which indicates that the `state[]` and
+`continue` values are understood.
++
+It is not obligatory to use the additional features just because the capability
+is supported, but they should not be provided without the capability.
+
+Unrecognised attributes and capabilities are silently discarded.
+
+[[CAPA-IOFMT]]
+CAPABILITY INPUT/OUTPUT FORMAT
+------------------------------
+
+For `git credential capability`, the format is slightly different. First, a
+`version 0` announcement is made to indicate the current version of the
+protocol, and then each capability is announced with a line like `capability
+authtype`. Credential helpers may also implement this format, again with the
+`capability` argument. Additional lines may be added in the future; callers
+should ignore lines which they don't understand.
+
+Because this is a new part of the credential helper protocol, older versions of
+Git, as well as some credential helpers, may not support it.  If a non-zero
+exit status is received, or if the first line doesn't start with the word
+`version` and a space, callers should assume that no capabilities are supported.
+
+The intention of this format is to differentiate it from the credential output
+in an unambiguous way.  It is possible to use very simple credential helpers
+(e.g., inline shell scripts) which always produce identical output.  Using a
+distinct format allows users to continue to use this syntax without having to
+worry about correctly implementing capability advertisements or accidentally
+confusing callers querying for capabilities.
 
 GIT
 ---
diff --git a/Documentation/git-for-each-repo.txt b/Documentation/git-for-each-repo.txt
index 94bd19d..abe3527 100644
--- a/Documentation/git-for-each-repo.txt
+++ b/Documentation/git-for-each-repo.txt
@@ -42,6 +42,15 @@
 as available. If `git for-each-repo` is run in a directory that is not a
 Git repository, then only the system and global config is used.
 
+--keep-going::
+	Continue with the remaining repositories if the command failed
+	on a repository. The exit code will still indicate that the
+	overall operation was not successful.
++
+Note that the exact exit code of the failing command is not passed
+through as the exit code of the `for-each-repo` command: If the command
+failed in any of the specified repositories, the overall exit code will
+be 1.
 
 SUBPROCESS BEHAVIOR
 -------------------
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 728bb38..8708b31 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -20,7 +20,7 @@
 		   [--in-reply-to=<message-id>] [--suffix=.<sfx>]
 		   [--ignore-if-in-upstream] [--always]
 		   [--cover-from-description=<mode>]
-		   [--rfc] [--subject-prefix=<subject-prefix>]
+		   [--rfc[=<rfc>]] [--subject-prefix=<subject-prefix>]
 		   [(--reroll-count|-v) <n>]
 		   [--to=<email>] [--cc=<email>]
 		   [--[no-]cover-letter] [--quiet]
@@ -238,10 +238,21 @@
 	value of the `format.filenameMaxLength` configuration
 	variable, or 64 if unconfigured.
 
---rfc::
-	Prepends "RFC" to the subject prefix (producing "RFC PATCH" by
-	default). RFC means "Request For Comments"; use this when sending
-	an experimental patch for discussion rather than application.
+--rfc[=<rfc>]::
+	Prepends the string _<rfc>_ (defaults to "RFC") to
+	the subject prefix.  As the subject prefix defaults to
+	"PATCH", you'll get "RFC PATCH" by default.
++
+RFC means "Request For Comments"; use this when sending
+an experimental patch for discussion rather than application.
+"--rfc=WIP" may also be a useful way to indicate that a patch
+is not complete yet ("WIP" stands for "Work In Progress").
++
+If the convention of the receiving community for a particular extra
+string is to have it _after_ the subject prefix, the string _<rfc>_
+can be prefixed with a dash ("`-`") to signal that the the rest of
+the _<rfc>_ string should be appended to the subject prefix instead,
+e.g., `--rfc='-(WIP)'` results in "PATCH (WIP)".
 
 -v <n>::
 --reroll-count=<n>::
@@ -346,6 +357,11 @@
 	between the previous and current series of patches by adjusting the
 	creation/deletion cost fudge factor. See linkgit:git-range-diff[1])
 	for details.
++
+Defaults to 999 (the linkgit:git-range-diff[1] uses 60), as the use
+case is to show comparison with an older iteration of the same
+topic and the tool should find more correspondence between the two
+sets of patches.
 
 --notes[=<ref>]::
 --no-notes::
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index e8f3ccb..f5b02ef 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -114,7 +114,7 @@
 
 The official repository of the 'git gui' project can be found at:
 
-  https://github.com/prati0100/git-gui.git/
+  https://github.com/j6t/git-gui
 
 GIT
 ---
diff --git a/Documentation/git-merge-tree.txt b/Documentation/git-merge-tree.txt
index dd388fa..84cb2ed 100644
--- a/Documentation/git-merge-tree.txt
+++ b/Documentation/git-merge-tree.txt
@@ -72,6 +72,11 @@
 As the merge-base is provided directly, <branch1> and <branch2> do not need
 to specify commits; trees are enough.
 
+-X<option>::
+--strategy-option=<option>::
+	Pass the merge strategy-specific option through to the merge strategy.
+	See linkgit:git-merge[1] for details.
+
 [[OUTPUT]]
 OUTPUT
 ------
diff --git a/Documentation/git-refs.txt b/Documentation/git-refs.txt
new file mode 100644
index 0000000..5b99e04
--- /dev/null
+++ b/Documentation/git-refs.txt
@@ -0,0 +1,61 @@
+git-refs(1)
+===========
+
+NAME
+----
+git-refs - Low-level access to refs
+
+
+SYNOPSIS
+--------
+[verse]
+'git refs migrate' --ref-format=<format> [--dry-run]
+
+DESCRIPTION
+-----------
+
+This command provides low-level access to refs.
+
+COMMANDS
+--------
+
+migrate::
+	Migrate ref store between different formats.
+
+OPTIONS
+-------
+
+The following options are specific to 'git refs migrate':
+
+--ref-format=<format>::
+	The ref format to migrate the ref store to. Can be one of:
++
+include::ref-storage-format.txt[]
+
+--dry-run::
+	Perform the migration, but do not modify the repository. The migrated
+	refs will be written into a separate directory that can be inspected
+	separately. The name of the directory will be reported on stdout. This
+	can be used to double check that the migration works as expected before
+	performing the actual migration.
+
+KNOWN LIMITATIONS
+-----------------
+
+The ref format migration has several known limitations in its current form:
+
+* It is not possible to migrate repositories that have reflogs.
+
+* It is not possible to migrate repositories that have worktrees.
+
+* There is no way to block concurrent writes to the repository during an
+  ongoing migration. Concurrent writes can lead to an inconsistent migrated
+  state. Users are expected to block writes on a higher level. If your
+  repository is registered for scheduled maintenance, it is recommended to
+  unregister it first with git-maintenance(1).
+
+These limitations may eventually be lifted.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index f9d5a35..dc12d38 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -18,8 +18,15 @@
 (i.e. parameters that begin with a dash '-') and parameters
 meant for the underlying 'git rev-list' command they use internally
 and flags and parameters for the other commands they use
-downstream of 'git rev-list'.  This command is used to
-distinguish between them.
+downstream of 'git rev-list'.  The primary purpose of this command
+is to allow calling programs to distinguish between them.  There are
+a few other operation modes that have nothing to do with the above
+"help parse command line options".
+
+Unless otherwise specified, most of the options and operation modes
+require you to run this command inside a git repository or a working
+tree that is under the control of a git repository, and will give you
+a fatal error otherwise.
 
 
 OPTIONS
@@ -32,11 +39,15 @@
 
 --parseopt::
 	Use 'git rev-parse' in option parsing mode (see PARSEOPT section below).
+	The command in this mode can be used outside a repository or
+	a working tree controlled by a repository.
 
 --sq-quote::
 	Use 'git rev-parse' in shell quoting mode (see SQ-QUOTE
 	section below). In contrast to the `--sq` option below, this
 	mode only does quoting. Nothing else is done to command input.
+	The command in this mode can be used outside a repository or
+	a working tree controlled by a repository.
 
 Options for --parseopt
 ~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 5fe519c..4494729 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,6 +10,7 @@
 --------
 [verse]
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]
+	[(--trailer <token>[(=|:)<value>])...]
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
@@ -31,8 +32,8 @@
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
-If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
-are absent, `-a` is implied.
+If `-m <msg>` or `-F <file>` or `--trailer <token>[=<value>]` is given
+and `-a`, `-s`, and `-u <key-id>` are absent, `-a` is implied.
 
 Otherwise, a tag reference that points directly at the given object
 (i.e., a lightweight tag) is created.
@@ -178,6 +179,17 @@
 	Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
 	is given.
 
+--trailer <token>[(=|:)<value>]::
+	Specify a (<token>, <value>) pair that should be applied as a
+	trailer. (e.g. `git tag --trailer "Custom-Key: value"`
+	will add a "Custom-Key" trailer to the tag message.)
+	The `trailer.*` configuration variables
+	(linkgit:git-interpret-trailers[1]) can be used to define if
+	a duplicated trailer is omitted, where in the run of trailers
+	each trailer would appear, and other details.
+	The trailers can be extracted in `git tag --list`, using
+	`--format="%(trailers)"` placeholder.
+
 -e::
 --edit::
 	The message taken from file with `-F` and command line with
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 8c47890..7128aed 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -25,6 +25,7 @@
 	     [--really-refresh] [--unresolve] [--again | -g]
 	     [--info-only] [--index-info]
 	     [-z] [--stdin] [--index-version <n>]
+	     [--show-index-version]
 	     [--verbose]
 	     [--] [<file>...]
 
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 7ad60bc..516d163 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -55,6 +55,37 @@
 	admins may need to configure some transports to allow this
 	variable to be passed. See the discussion in linkgit:git[1].
 
+`GIT_NO_LAZY_FETCH`::
+	When cloning or fetching from a partial repository (i.e., one
+	itself cloned with `--filter`), the server-side `upload-pack`
+	may need to fetch extra objects from its upstream in order to
+	complete the request. By default, `upload-pack` will refuse to
+	perform such a lazy fetch, because `git fetch` may run arbitrary
+	commands specified in configuration and hooks of the source
+	repository (and `upload-pack` tries to be safe to run even in
+	untrusted `.git` directories).
++
+This is implemented by having `upload-pack` internally set the
+`GIT_NO_LAZY_FETCH` variable to `1`. If you want to override it
+(because you are fetching from a partial clone, and you are sure
+you trust it), you can explicitly set `GIT_NO_LAZY_FETCH` to
+`0`.
+
+SECURITY
+--------
+
+Most Git commands should not be run in an untrusted `.git` directory
+(see the section `SECURITY` in linkgit:git[1]). `upload-pack` tries to
+avoid any dangerous configuration options or hooks from the repository
+it's serving, making it safe to clone an untrusted directory and run
+commands on the resulting clone.
+
+For an extra level of safety, you may be able to run `upload-pack` as an
+alternate user. The details will be platform dependent, but on many
+systems you can run:
+
+    git clone --no-local --upload-pack='sudo -u nobody git-upload-pack' ...
+
 SEE ALSO
 --------
 linkgit:gitnamespaces[7]
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 7a1b112..a31a70a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -11,9 +11,10 @@
 [verse]
 'git' [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
     [--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>]
-    [--config-env=<name>=<envvar>] <command> [<args>]
+    [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
+    [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
+    [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
+    <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -186,6 +187,13 @@
 	This is equivalent to setting the `GIT_NO_LAZY_FETCH`
 	environment variable to `1`.
 
+--no-optional-locks::
+	Do not perform optional operations that require locks. This is
+	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
+
+--no-advice::
+	Disable all advice hints from being printed.
+
 --literal-pathspecs::
 	Treat pathspecs literally (i.e. no globbing, no pathspec magic).
 	This is equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
@@ -207,10 +215,6 @@
 	Add "icase" magic to all pathspec. This is equivalent to setting
 	the `GIT_ICASE_PATHSPECS` environment variable to `1`.
 
---no-optional-locks::
-	Do not perform optional operations that require locks. This is
-	equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
-
 --list-cmds=<group>[,<group>...]::
 	List commands by group. This is an internal/experimental
 	option and may change or be removed in the future. Supported
@@ -1067,6 +1071,37 @@
 for a given pathname.  These stages are used to hold the various
 unmerged version of a file when a merge is in progress.
 
+SECURITY
+--------
+
+Some configuration options and hook files may cause Git to run arbitrary
+shell commands. Because configuration and hooks are not copied using
+`git clone`, it is generally safe to clone remote repositories with
+untrusted content, inspect them with `git log`, and so on.
+
+However, it is not safe to run Git commands in a `.git` directory (or
+the working tree that surrounds it) when that `.git` directory itself
+comes from an untrusted source. The commands in its config and hooks
+are executed in the usual way.
+
+By default, Git will refuse to run when the repository is owned by
+someone other than the user running the command. See the entry for
+`safe.directory` in linkgit:git-config[1]. While this can help protect
+you in a multi-user environment, note that you can also acquire
+untrusted repositories that are owned by you (for example, if you
+extract a zip file or tarball from an untrusted source). In such cases,
+you'd need to "sanitize" the untrusted repository first.
+
+If you have an untrusted `.git` directory, you should first clone it
+with `git clone --no-local` to obtain a clean copy. Git does restrict
+the set of options and hooks that will be run by `upload-pack`, which
+handles the server side of a clone or fetch, but beware that the
+surface area for attack against `upload-pack` is large, so this does
+carry some risk. The safest thing is to serve the repository as an
+unprivileged user (either via linkgit:git-daemon[1], ssh, or using
+other tools to change user ids). See the discussion in the `SECURITY`
+section of linkgit:git-upload-pack[1].
+
 FURTHER DOCUMENTATION
 ---------------------
 
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index ee9b92c..06e9971 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -486,7 +486,7 @@
 This hook is invoked by any Git command that performs reference
 updates. It executes whenever a reference transaction is prepared,
 committed or aborted and may thus get called multiple times. The hook
-does not cover symbolic references (but that may change in the future).
+also supports symbolic reference updates.
 
 The hook takes exactly one argument, which is the current state the
 given reference transaction is in:
@@ -503,16 +503,20 @@
 For each reference update that was added to the transaction, the hook
 receives on standard input a line of the format:
 
-  <old-oid> SP <new-oid> SP <ref-name> LF
+  <old-value> SP <new-value> SP <ref-name> LF
 
-where `<old-oid>` is the old object name passed into the reference
-transaction, `<new-oid>` is the new object name to be stored in the
+where `<old-value>` is the old object name passed into the reference
+transaction, `<new-value>` is the new object name to be stored in the
 ref and `<ref-name>` is the full name of the ref. When force updating
 the reference regardless of its current value or when the reference is
-to be created anew, `<old-oid>` is the all-zeroes object name. To
+to be created anew, `<old-value>` is the all-zeroes object name. To
 distinguish these cases, you can inspect the current value of
 `<ref-name>` via `git rev-parse`.
 
+For symbolic reference updates the `<old_value>` and `<new-value>`
+fields could denote references instead of objects. A reference will be
+denoted with a 'ref:' prefix, like `ref:<ref-target>`.
+
 The exit status of the hook is ignored for any state except for the
 "prepared" state. In the "prepared" state, a non-zero exit status will
 cause the transaction to be aborted. The hook will not be called with
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index d71b199..30b394a 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -497,20 +497,18 @@
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	Pseudorefs are a class of files under `$GIT_DIR` which behave
-	like refs for the purposes of rev-parse, but which are treated
-	specially by git.  Pseudorefs both have names that are all-caps,
-	and always start with a line consisting of a
-	<<def_SHA1,SHA-1>> followed by whitespace.  So, HEAD is not a
-	pseudoref, because it is sometimes a symbolic ref.  They might
-	optionally contain some additional data.  `MERGE_HEAD` and
-	`CHERRY_PICK_HEAD` are examples.  Unlike
-	<<def_per_worktree_ref,per-worktree refs>>, these files cannot
-	be symbolic refs, and never have reflogs.  They also cannot be
-	updated through the normal ref update machinery.  Instead,
-	they are updated by directly writing to the files.  However,
-	they can be read as if they were refs, so `git rev-parse
-	MERGE_HEAD` will work.
+	A ref that has different semantics than normal refs. These refs can be
+	read via normal Git commands, but cannot be written to by commands like
+	linkgit:git-update-ref[1].
++
+The following pseudorefs are known to Git:
+
+ - `FETCH_HEAD` is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+   may refer to multiple object IDs. Each object ID is annotated with metadata
+   indicating where it was fetched from and its fetch status.
+
+ - `MERGE_HEAD` is written by linkgit:git-merge[1] when resolving merge
+   conflicts. It contains all commit IDs which are being merged.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -552,20 +550,38 @@
 	to the result.
 
 [[def_ref]]ref::
-	A name that begins with `refs/` (e.g. `refs/heads/master`)
-	that points to an <<def_object_name,object name>> or another
-	ref (the latter is called a <<def_symref,symbolic ref>>).
+	A name that that points to an <<def_object_name,object name>> or
+	another ref (the latter is called a <<def_symref,symbolic ref>>).
 	For convenience, a ref can sometimes be abbreviated when used
 	as an argument to a Git command; see linkgit:gitrevisions[7]
 	for details.
 	Refs are stored in the <<def_repository,repository>>.
 +
 The ref namespace is hierarchical.
-Different subhierarchies are used for different purposes (e.g. the
-`refs/heads/` hierarchy is used to represent local branches).
+Ref names must either start with `refs/` or be located in the root of
+the hierarchy. For the latter, their name must follow these rules:
 +
-There are a few special-purpose refs that do not begin with `refs/`.
-The most notable example is `HEAD`.
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
++
+There are some irregular refs in the root of the hierarchy that do not
+match these rules. The following list is exhaustive and shall not be
+extended in the future:
++
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
++
+Different subhierarchies are used for different purposes. For example,
+the `refs/heads/` hierarchy is used to represent local branches whereas
+the `refs/tags/` hierarchy is used to represent local tags..
 
 [[def_reflog]]reflog::
 	A reflog shows the local "history" of a ref.  In other words,
@@ -576,7 +592,8 @@
 [[def_refspec]]refspec::
 	A "refspec" is used by <<def_fetch,fetch>> and
 	<<def_push,push>> to describe the mapping between remote
-	<<def_ref,ref>> and local ref.
+	<<def_ref,ref>> and local ref. See linkgit:git-fetch[1] or
+	linkgit:git-push[1] for details.
 
 [[def_remote]]remote repository::
 	A <<def_repository,repository>> which is used to track the same
@@ -638,20 +655,6 @@
 	An <<def_object,object>> used to temporarily store the contents of a
 	<<def_dirty,dirty>> working directory and the index for future reuse.
 
-[[def_special_ref]]special ref::
-	A ref that has different semantics than normal refs. These refs can be
-	accessed via normal Git commands but may not behave the same as a
-	normal ref in some cases.
-+
-The following special refs are known to Git:
-
- - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
-   may refer to multiple object IDs. Each object ID is annotated with metadata
-   indicating where it was fetched from and its fetch status.
-
- - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
-   conflicts. It contains all commit IDs which are being merged.
-
 [[def_submodule]]submodule::
 	A <<def_repository,repository>> that holds the history of a
 	separate project inside another repository (the latter of
diff --git a/Documentation/lint-manpages.sh b/Documentation/lint-manpages.sh
new file mode 100755
index 0000000..92cfc0a
--- /dev/null
+++ b/Documentation/lint-manpages.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+extract_variable () {
+	(
+		cat ../Makefile
+		cat <<EOF
+print_variable:
+	@\$(foreach b,\$($1),echo XXX \$(b:\$X=) YYY;)
+EOF
+	) |
+	make -C .. -f - print_variable 2>/dev/null |
+	sed -n -e 's/.*XXX \(.*\) YYY.*/\1/p'
+}
+
+check_missing_docs () (
+	ret=0
+
+	for v in $ALL_COMMANDS
+	do
+		case "$v" in
+		git-merge-octopus) continue;;
+		git-merge-ours) continue;;
+		git-merge-recursive) continue;;
+		git-merge-resolve) continue;;
+		git-merge-subtree) continue;;
+		git-fsck-objects) continue;;
+		git-init-db) continue;;
+		git-remote-*) continue;;
+		git-stage) continue;;
+		git-legacy-*) continue;;
+		git-?*--?* ) continue ;;
+		esac
+
+		if ! test -f "$v.txt"
+		then
+			echo "no doc: $v"
+			ret=1
+		fi
+
+		if ! sed -e '1,/^### command list/d' -e '/^#/d' ../command-list.txt |
+			grep -q "^$v[ 	]"
+		then
+			case "$v" in
+			git)
+				;;
+			*)
+				echo "no link: $v"
+				ret=1
+				;;
+			esac
+		fi
+	done
+
+	exit $ret
+)
+
+check_extraneous_docs () {
+	(
+		sed -e '1,/^### command list/d' \
+		    -e '/^#/d' \
+		    -e '/guide$/d' \
+		    -e '/interfaces$/d' \
+		    -e 's/[ 	].*//' \
+		    -e 's/^/listed /' ../command-list.txt
+		make print-man1 |
+		grep '\.txt$' |
+		sed -e 's|^|documented |' \
+		    -e 's/\.txt//'
+	) | (
+		all_commands="$(printf "%s " "$ALL_COMMANDS" "$BUILT_INS" "$EXCLUDED_PROGRAMS" | tr '\n' ' ')"
+		ret=0
+
+		while read how cmd
+		do
+			case " $all_commands " in
+			*" $cmd "*) ;;
+			*)
+				echo "removed but $how: $cmd"
+				ret=1;;
+			esac
+		done
+
+		exit $ret
+	)
+}
+
+BUILT_INS="$(extract_variable BUILT_INS)"
+ALL_COMMANDS="$(extract_variable ALL_COMMANDS)"
+EXCLUDED_PROGRAMS="$(extract_variable EXCLUDED_PROGRAMS)"
+
+findings=$(
+	if ! check_missing_docs
+	then
+		ret=1
+	fi
+
+	if ! check_extraneous_docs
+	then
+		ret=1
+	fi
+
+	exit $ret
+)
+ret=$?
+
+printf "%s" "$findings" | sort
+
+exit $ret
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 433cf41..814cdcf 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.45.0
+DEF_VER=v2.45.GIT
 
 LF='
 '
diff --git a/Makefile b/Makefile
index 1e31acc..990aa3c 100644
--- a/Makefile
+++ b/Makefile
@@ -409,6 +409,9 @@
 # to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
 # that implements the `fsm_os_settings__*()` routines.
 #
+# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
+# programs in oss-fuzz/.
+#
 # === Optional library: libintl ===
 #
 # Define NO_GETTEXT if you don't want Git output to be translated.
@@ -752,23 +755,6 @@
 
 ETAGS_TARGET = TAGS
 
-# If you add a new fuzzer, please also make sure to run it in
-# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
-# runs in the future.
-FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
-FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
-FUZZ_OBJS += oss-fuzz/fuzz-config.o
-FUZZ_OBJS += oss-fuzz/fuzz-date.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
-FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
-.PHONY: fuzz-objs
-fuzz-objs: $(FUZZ_OBJS)
-
-# Always build fuzz objects even if not testing, to prevent bit-rot.
-all:: $(FUZZ_OBJS)
-
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
-
 # Empty...
 EXTRA_PROGRAMS =
 
@@ -807,7 +793,7 @@
 TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-env-helper.o
-TEST_BUILTINS_OBJS += test-example-decorate.o
+TEST_BUILTINS_OBJS += test-example-tap.o
 TEST_BUILTINS_OBJS += test-find-pack.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
 TEST_BUILTINS_OBJS += test-genrandom.o
@@ -852,7 +838,6 @@
 TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-simple-ipc.o
-TEST_BUILTINS_OBJS += test-strcmp-offset.o
 TEST_BUILTINS_OBJS += test-string-list.o
 TEST_BUILTINS_OBJS += test-submodule-config.o
 TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
@@ -1296,6 +1281,7 @@
 BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
+BUILTIN_OBJS += builtin/refs.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
 BUILTIN_OBJS += builtin/remote.o
@@ -1347,11 +1333,16 @@
 THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%
 
-UNIT_TEST_PROGRAMS += t-basic
-UNIT_TEST_PROGRAMS += t-mem-pool
-UNIT_TEST_PROGRAMS += t-strbuf
 UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGRAMS += t-example-decorate
+UNIT_TEST_PROGRAMS += t-hash
+UNIT_TEST_PROGRAMS += t-mem-pool
 UNIT_TEST_PROGRAMS += t-prio-queue
+UNIT_TEST_PROGRAMS += t-reftable-basics
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-strcmp-offset
+UNIT_TEST_PROGRAMS += t-strvec
+UNIT_TEST_PROGRAMS += t-trailer
 UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
 UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
@@ -2373,6 +2364,29 @@
 endif
 	$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
+FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-config.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
+.PHONY: fuzz-objs
+fuzz-objs: $(FUZZ_OBJS)
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
+
+# Build fuzz programs when possible, even without the necessary fuzzing support,
+# to prevent bit-rot.
+ifdef LINK_FUZZ_PROGRAMS
+all:: $(FUZZ_PROGRAMS)
+endif
+
 please_set_SHELL_PATH_to_a_more_modern_shell:
 	@$$(:)
 
@@ -2656,20 +2670,17 @@
 REFTABLE_OBJS += reftable/pq.o
 REFTABLE_OBJS += reftable/reader.o
 REFTABLE_OBJS += reftable/record.o
-REFTABLE_OBJS += reftable/refname.o
 REFTABLE_OBJS += reftable/generic.o
 REFTABLE_OBJS += reftable/stack.o
 REFTABLE_OBJS += reftable/tree.o
 REFTABLE_OBJS += reftable/writer.o
 
-REFTABLE_TEST_OBJS += reftable/basics_test.o
 REFTABLE_TEST_OBJS += reftable/block_test.o
 REFTABLE_TEST_OBJS += reftable/dump.o
 REFTABLE_TEST_OBJS += reftable/merged_test.o
 REFTABLE_TEST_OBJS += reftable/pq_test.o
 REFTABLE_TEST_OBJS += reftable/record_test.o
 REFTABLE_TEST_OBJS += reftable/readwrite_test.o
-REFTABLE_TEST_OBJS += reftable/refname_test.o
 REFTABLE_TEST_OBJS += reftable/stack_test.o
 REFTABLE_TEST_OBJS += reftable/test_framework.o
 REFTABLE_TEST_OBJS += reftable/tree_test.o
@@ -3228,7 +3239,7 @@
 
 .PRECIOUS: $(TEST_OBJS)
 
-t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
+t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
 
 t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
@@ -3749,42 +3760,6 @@
 .PHONY: check-docs
 check-docs::
 	$(MAKE) -C Documentation lint-docs
-	@(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
-	do \
-		case "$$v" in \
-		git-merge-octopus | git-merge-ours | git-merge-recursive | \
-		git-merge-resolve | git-merge-subtree | \
-		git-fsck-objects | git-init-db | \
-		git-remote-* | git-stage | git-legacy-* | \
-		git-?*--?* ) continue ;; \
-		esac ; \
-		test -f "Documentation/$$v.txt" || \
-		echo "no doc: $$v"; \
-		sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
-		grep -q "^$$v[ 	]" || \
-		case "$$v" in \
-		git) ;; \
-		*) echo "no link: $$v";; \
-		esac ; \
-	done; \
-	( \
-		sed -e '1,/^### command list/d' \
-		    -e '/^#/d' \
-		    -e '/guide$$/d' \
-		    -e '/interfaces$$/d' \
-		    -e 's/[ 	].*//' \
-		    -e 's/^/listed /' command-list.txt; \
-		$(MAKE) -C Documentation print-man1 | \
-		grep '\.txt$$' | \
-		sed -e 's|^|documented |' \
-		    -e 's/\.txt//'; \
-	) | while read how cmd; \
-	do \
-		case " $(patsubst %$X,%,$(ALL_COMMANDS) $(BUILT_INS) $(EXCLUDED_PROGRAMS)) " in \
-		*" $$cmd "*)	;; \
-		*) echo "removed but $$how: $$cmd" ;; \
-		esac; \
-	done ) | sort
 
 ### Make sure built-ins do not have dups and listed in git.c
 #
@@ -3858,22 +3833,22 @@
 #
 # An example command to build against libFuzzer from LLVM 11.0.0:
 #
-# make CC=clang CXX=clang++ \
+# make CC=clang FUZZ_CXX=clang++ \
 #      CFLAGS="-fsanitize=fuzzer-no-link,address" \
 #      LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
 #      fuzz-all
 #
+FUZZ_CXX ?= $(CC)
 FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
 
 .PHONY: fuzz-all
+fuzz-all: $(FUZZ_PROGRAMS)
 
 $(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
-	$(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+	$(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
 		-Wl,--allow-multiple-definition \
 		$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
 
-fuzz-all: $(FUZZ_PROGRAMS)
-
 $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
 	$(call mkdir_p_parent_template)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
@@ -3881,5 +3856,5 @@
 
 .PHONY: build-unit-tests unit-tests
 build-unit-tests: $(UNIT_TEST_PROGS)
-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X
 	$(MAKE) -C t/ unit-tests
diff --git a/RelNotes b/RelNotes
index ae70277..cc696fc 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.45.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.46.0.txt
\ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index e17602b..b5d6cd6 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -532,8 +532,9 @@
 			      size_t *binary_count)
 {
 	struct object_id head_oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-					     &head_oid, NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &head_oid, NULL);
 	struct collection_status s = { 0 };
 	int i;
 
@@ -761,8 +762,10 @@
 	size_t count, i, j;
 
 	struct object_id oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
-					     NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &oid,
+						  NULL);
 	struct lock_file index_lock;
 	const char **paths;
 	struct tree *tree;
@@ -990,8 +993,10 @@
 	ssize_t count, i;
 
 	struct object_id oid;
-	int is_initial = !resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &oid,
-					     NULL);
+	int is_initial = !refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  "HEAD", RESOLVE_REF_READING,
+						  &oid,
+						  NULL);
 	if (get_modified_files(s->r, INDEX_ONLY, files, ps, NULL, NULL) < 0)
 		return -1;
 
diff --git a/add-patch.c b/add-patch.c
index b2fbd8c..d8ea05f 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -293,10 +293,9 @@
 	va_list args;
 
 	va_start(args, fmt);
-	fputs(s->s.error_color, stderr);
-	vfprintf(stderr, fmt, args);
-	fputs(s->s.reset_color, stderr);
-	fputc('\n', stderr);
+	fputs(s->s.error_color, stdout);
+	vprintf(fmt, args);
+	puts(s->s.reset_color);
 	va_end(args);
 }
 
@@ -1229,6 +1228,7 @@
 		fflush(stdout);
 		if (read_single_character(s) == EOF)
 			return -1;
+		/* do not limit to 1-byte input to allow 'no' etc. */
 		switch (tolower(s->answer.buf[0])) {
 		case 'n': return 0;
 		case 'y': return 1;
@@ -1327,7 +1327,7 @@
 		err(s, _("Nothing was applied.\n"));
 	} else
 		/* As a last resort, show the diff to the user */
-		fwrite(diff->buf, diff->len, 1, stderr);
+		fwrite(diff->buf, diff->len, 1, stdout);
 
 	return 0;
 }
@@ -1512,6 +1512,12 @@
 		if (!s->answer.len)
 			continue;
 		ch = tolower(s->answer.buf[0]);
+
+		/* 'g' takes a hunk number and '/' takes a regexp */
+		if (s->answer.len != 1 && (ch != 'g' && ch != '/')) {
+			err(s, _("Only one letter is expected, got '%s'"), s->answer.buf);
+			continue;
+		}
 		if (ch == 'y') {
 			hunk->use = USE_HUNK;
 soft_increment:
@@ -1669,7 +1675,7 @@
 			}
 		} else if (s->answer.buf[0] == 'p') {
 			rendered_hunk_index = -1;
-		} else {
+		} else if (s->answer.buf[0] == '?') {
 			const char *p = _(help_patch_remainder), *eol = p;
 
 			color_fprintf(stdout, s->s.help_color, "%s",
@@ -1693,6 +1699,9 @@
 				color_fprintf_ln(stdout, s->s.help_color,
 						 "%.*s", (int)(eol - p), p);
 			}
+		} else {
+			err(s, _("Unknown command '%s' (use '?' for help)"),
+			    s->answer.buf);
 		}
 	}
 
@@ -1779,9 +1788,9 @@
 			break;
 
 	if (s.file_diff_nr == 0)
-		fprintf(stderr, _("No changes.\n"));
+		err(&s, _("No changes."));
 	else if (binary_count == s.file_diff_nr)
-		fprintf(stderr, _("Only binary files changed.\n"));
+		err(&s, _("Only binary files changed."));
 
 	add_p_state_clear(&s);
 	return 0;
diff --git a/advice.c b/advice.c
index 7511119..0a122c2 100644
--- a/advice.c
+++ b/advice.c
@@ -2,6 +2,7 @@
 #include "advice.h"
 #include "config.h"
 #include "color.h"
+#include "environment.h"
 #include "gettext.h"
 #include "help.h"
 #include "string-list.h"
@@ -127,6 +128,12 @@
 int advice_enabled(enum advice_type type)
 {
 	int enabled = advice_setting[type].level != ADVICE_LEVEL_DISABLED;
+	static int globally_enabled = -1;
+
+	if (globally_enabled < 0)
+		globally_enabled = git_env_bool(GIT_ADVICE_ENVIRONMENT, 1);
+	if (!globally_enabled)
+		return 0;
 
 	if (type == ADVICE_PUSH_UPDATE_REJECTED)
 		return enabled &&
diff --git a/alias.c b/alias.c
index 5a238f2..4daafd9 100644
--- a/alias.c
+++ b/alias.c
@@ -21,9 +21,11 @@
 		return 0;
 
 	if (data->alias) {
-		if (!strcasecmp(p, data->alias))
-			return git_config_string((const char **)&data->v,
+		if (!strcasecmp(p, data->alias)) {
+			FREE_AND_NULL(data->v);
+			return git_config_string(&data->v,
 						 key, value);
+		}
 	} else if (data->list) {
 		string_list_append(data->list, p);
 	}
diff --git a/attr.c b/attr.c
index 679e422..300f994 100644
--- a/attr.c
+++ b/attr.c
@@ -25,7 +25,7 @@
 #include "tree-walk.h"
 #include "object-name.h"
 
-const char *git_attr_tree;
+char *git_attr_tree;
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -765,8 +765,8 @@
 	return res;
 }
 
-static struct attr_stack *read_attr_from_buf(char *buf, const char *path,
-					     unsigned flags)
+static struct attr_stack *read_attr_from_buf(char *buf, size_t length,
+					     const char *path, unsigned flags)
 {
 	struct attr_stack *res;
 	char *sp;
@@ -774,6 +774,11 @@
 
 	if (!buf)
 		return NULL;
+	if (length >= ATTR_MAX_FILE_SIZE) {
+		warning(_("ignoring overly large gitattributes blob '%s'"), path);
+		free(buf);
+		return NULL;
+	}
 
 	CALLOC_ARRAY(res, 1);
 	for (sp = buf; *sp;) {
@@ -813,7 +818,7 @@
 		return NULL;
 	}
 
-	return read_attr_from_buf(buf, path, flags);
+	return read_attr_from_buf(buf, sz, path, flags);
 }
 
 static struct attr_stack *read_attr_from_index(struct index_state *istate,
@@ -860,13 +865,7 @@
 		stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags);
 	} else {
 		buf = read_blob_data_from_index(istate, path, &size);
-		if (!buf)
-			return NULL;
-		if (size >= ATTR_MAX_FILE_SIZE) {
-			warning(_("ignoring overly large gitattributes blob '%s'"), path);
-			return NULL;
-		}
-		stack = read_attr_from_buf(buf, path, flags);
+		stack = read_attr_from_buf(buf, size, path, flags);
 	}
 	return stack;
 }
@@ -1206,15 +1205,16 @@
 }
 
 static const char *default_attr_source_tree_object_name;
-static int ignore_bad_attr_tree;
 
 void set_git_attr_source(const char *tree_object_name)
 {
 	default_attr_source_tree_object_name = xstrdup(tree_object_name);
 }
 
-static void compute_default_attr_source(struct object_id *attr_source)
+static int compute_default_attr_source(struct object_id *attr_source)
 {
+	int ignore_bad_attr_tree = 0;
+
 	if (!default_attr_source_tree_object_name)
 		default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
 
@@ -1223,29 +1223,34 @@
 		ignore_bad_attr_tree = 1;
 	}
 
-	if (!default_attr_source_tree_object_name &&
-	    startup_info->have_repository &&
-	    is_bare_repository()) {
-		default_attr_source_tree_object_name = "HEAD";
-		ignore_bad_attr_tree = 1;
-	}
+	if (!default_attr_source_tree_object_name)
+		return 0;
 
-	if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
-		return;
+	if (!startup_info->have_repository) {
+		if (!ignore_bad_attr_tree)
+			die(_("cannot use --attr-source or GIT_ATTR_SOURCE without repo"));
+		return 0;
+	}
 
 	if (repo_get_oid_treeish(the_repository,
 				 default_attr_source_tree_object_name,
-				 attr_source) && !ignore_bad_attr_tree)
-		die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+				 attr_source)) {
+		if (!ignore_bad_attr_tree)
+			die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+		return 0;
+	}
+
+	return 1;
 }
 
 static struct object_id *default_attr_source(void)
 {
 	static struct object_id attr_source;
+	static int has_attr_source = -1;
 
-	if (is_null_oid(&attr_source))
-		compute_default_attr_source(&attr_source);
-	if (is_null_oid(&attr_source))
+	if (has_attr_source < 0)
+		has_attr_source = compute_default_attr_source(&attr_source);
+	if (!has_attr_source)
 		return NULL;
 	return &attr_source;
 }
@@ -1296,7 +1301,8 @@
 			if (pos >= 0) {
 				 if (S_ISGITLINK(istate->cache[pos]->ce_mode))
 					 mode = istate->cache[pos]->ce_mode;
-			} else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+			} else if (repo_resolve_gitlink_ref(the_repository, path,
+							    "HEAD", &oid) == 0) {
 				mode = S_IFGITLINK;
 			}
 		}
diff --git a/attr.h b/attr.h
index afa33ce..bb33b60 100644
--- a/attr.h
+++ b/attr.h
@@ -238,6 +238,6 @@
 /* Return whether the system gitattributes file is enabled and should be used. */
 int git_attr_system_is_enabled(void);
 
-extern const char *git_attr_tree;
+extern char *git_attr_tree;
 
 #endif /* ATTR_H */
diff --git a/bisect.c b/bisect.c
index 29aae87..4ea703b 100644
--- a/bisect.c
+++ b/bisect.c
@@ -469,7 +469,8 @@
 
 static int read_bisect_refs(void)
 {
-	return for_each_ref_in("refs/bisect/", register_ref, NULL);
+	return refs_for_each_ref_in(get_main_ref_store(the_repository),
+				    "refs/bisect/", register_ref, NULL);
 }
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -709,7 +710,7 @@
 static int is_expected_rev(const struct object_id *oid)
 {
 	struct object_id expected_oid;
-	if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected_oid))
 		return 0;
 	return oideq(oid, &expected_oid);
 }
@@ -721,11 +722,14 @@
 	struct pretty_print_context pp = {0};
 	struct strbuf commit_msg = STRBUF_INIT;
 
-	update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), NULL,
+			"BISECT_EXPECTED_REV", bisect_rev, NULL, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (no_checkout) {
-		update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), NULL,
+				"BISECT_HEAD", bisect_rev, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else {
 		struct child_process cmd = CHILD_PROCESS_INIT;
 
@@ -1027,7 +1031,8 @@
 	 * If no_checkout is non-zero, the bisection process does not
 	 * checkout the trial commit but instead simply updates BISECT_HEAD.
 	 */
-	int no_checkout = ref_exists("BISECT_HEAD");
+	int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+					  "BISECT_HEAD");
 	unsigned bisect_flags = 0;
 
 	read_bisect_terms(&term_bad, &term_good);
@@ -1178,10 +1183,14 @@
 
 	/* There may be some refs packed during bisection */
 	struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
-	for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+	refs_for_each_ref_in(get_main_ref_store(the_repository),
+			     "refs/bisect", mark_for_removal,
+			     (void *) &refs_for_removal);
 	string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
 	string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
-	result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
+	result = refs_delete_refs(get_main_ref_store(the_repository),
+				  "bisect: remove", &refs_for_removal,
+				  REF_NO_DEREF);
 	refs_for_removal.strdup_strings = 1;
 	string_list_clear(&refs_for_removal, 0);
 	unlink_or_warn(git_path_bisect_ancestors_ok());
diff --git a/blame.c b/blame.c
index 1a16d4e..33586b9 100644
--- a/blame.c
+++ b/blame.c
@@ -2700,7 +2700,7 @@
 		return NULL;
 
 	/* Do we have HEAD? */
-	if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+	if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
 		return NULL;
 	head_commit = lookup_commit_reference_gently(revs->repo,
 						     &head_oid, 1);
@@ -2803,7 +2803,7 @@
 		if (sb->final) {
 			parent_oid = &sb->final->object.oid;
 		} else {
-			if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+			if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL))
 				die("no such ref: HEAD");
 			parent_oid = &head_oid;
 		}
diff --git a/branch.c b/branch.c
index e4a738f..df5d24f 100644
--- a/branch.c
+++ b/branch.c
@@ -377,7 +377,7 @@
 		exit(code);
 	}
 
-	return ref_exists(ref->buf);
+	return refs_ref_exists(get_main_ref_store(the_repository), ref->buf);
 }
 
 static int initialized_checked_out_branches;
@@ -623,11 +623,12 @@
 		msg = xstrfmt("branch: Reset to %s", start_name);
 	else
 		msg = xstrfmt("branch: Created from %s", start_name);
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 		ref_transaction_update(transaction, ref.buf,
 					&oid, forcing ? NULL : null_oid(),
-					0, msg, &err) ||
+					NULL, NULL, 0, msg, &err) ||
 		ref_transaction_commit(transaction, &err))
 		die("%s", err.buf);
 	ref_transaction_free(transaction);
diff --git a/builtin.h b/builtin.h
index 2828063..7eda9b2 100644
--- a/builtin.h
+++ b/builtin.h
@@ -207,6 +207,7 @@
 int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
 int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 int cmd_reflog(int argc, const char **argv, const char *prefix);
+int cmd_refs(int argc, const char **argv, const char *prefix);
 int cmd_remote(int argc, const char **argv, const char *prefix);
 int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 int cmd_remote_fd(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index b7d3ff1..3dfcfc5 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Linus Torvalds
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -40,20 +40,20 @@
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 		int err;
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
 
 		if (!show_only)
-			err = chmod_index_entry(&the_index, ce, flip);
+			err = chmod_index_entry(the_repository->index, ce, flip);
 		else
 			err = S_ISREG(ce->ce_mode) ? 0 : -1;
 
@@ -68,20 +68,20 @@
 {
 	int i, retval = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
 		if (ce_stage(ce))
 			continue; /* do not touch unmerged paths */
 		if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode))
 			continue; /* do not touch non blobs */
-		if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
+		if (pathspec && !ce_path_match(the_repository->index, ce, pathspec, NULL))
 			continue;
-		retval |= add_file_to_index(&the_index, ce->name,
+		retval |= add_file_to_index(the_repository->index, ce->name,
 					    flags | ADD_CACHE_RENORMALIZE);
 	}
 
@@ -100,11 +100,11 @@
 	i = dir->nr;
 	while (--i >= 0) {
 		struct dir_entry *entry = *src++;
-		if (dir_path_match(&the_index, entry, pathspec, prefix, seen))
+		if (dir_path_match(the_repository->index, entry, pathspec, prefix, seen))
 			*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
-	add_pathspec_matches_against_index(pathspec, &the_index, seen,
+	add_pathspec_matches_against_index(pathspec, the_repository->index, seen,
 					   PS_IGNORE_SKIP_WORKTREE);
 	return seen;
 }
@@ -119,14 +119,14 @@
 		    (verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
 
 	seen = xcalloc(pathspec->nr, 1);
-	refresh_index(&the_index, flags, pathspec, seen,
+	refresh_index(the_repository->index, flags, pathspec, seen,
 		      _("Unstaged changes after refreshing the index:"));
 	for (i = 0; i < pathspec->nr; i++) {
 		if (!seen[i]) {
 			const char *path = pathspec->items[i].original;
 
 			if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
-			    !path_in_sparse_checkout(path, &the_index)) {
+			    !path_in_sparse_checkout(path, the_repository->index)) {
 				string_list_append(&only_match_skip_worktree,
 						   pathspec->items[i].original);
 			} else {
@@ -338,12 +338,12 @@
 
 	for (i = 0; i < dir->nr; i++) {
 		if (!include_sparse &&
-		    !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+		    !path_in_sparse_checkout(dir->entries[i]->name, the_repository->index)) {
 			string_list_append(&matched_sparse_paths,
 					   dir->entries[i]->name);
 			continue;
 		}
-		if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
+		if (add_file_to_index(the_repository->index, dir->entries[i]->name, flags)) {
 			if (!ignore_add_errors)
 				die(_("adding files failed"));
 			exit_status = 1;
@@ -461,8 +461,8 @@
 	if (repo_read_index_preload(the_repository, &pathspec, 0) < 0)
 		die(_("index file corrupt"));
 
-	die_in_unpopulated_submodule(&the_index, prefix);
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_in_unpopulated_submodule(the_repository->index, prefix);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	if (add_new_files) {
 		int baselen;
@@ -474,7 +474,7 @@
 		}
 
 		/* This picks up the paths that are not tracked */
-		baselen = fill_directory(&dir, &the_index, &pathspec);
+		baselen = fill_directory(&dir, the_repository->index, &pathspec);
 		if (pathspec.nr)
 			seen = prune_directory(&dir, &pathspec, baselen);
 	}
@@ -491,7 +491,7 @@
 
 		if (!seen)
 			seen = find_pathspecs_matching_against_index(&pathspec,
-					&the_index, PS_IGNORE_SKIP_WORKTREE);
+					the_repository->index, PS_IGNORE_SKIP_WORKTREE);
 
 		/*
 		 * file_exists() assumes exact match
@@ -527,8 +527,8 @@
 			    !file_exists(path)) {
 				if (ignore_missing) {
 					int dtype = DT_UNKNOWN;
-					if (is_excluded(&dir, &the_index, path, &dtype))
-						dir_add_ignored(&dir, &the_index,
+					if (is_excluded(&dir, the_repository->index, path, &dtype))
+						dir_add_ignored(&dir, the_repository->index,
 								path, pathspec.items[i].len);
 				} else
 					die(_("pathspec '%s' did not match any files"),
@@ -569,7 +569,7 @@
 	end_odb_transaction();
 
 finish:
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write new index file"));
 
diff --git a/builtin/am.c b/builtin/am.c
index 022cae2..3683902 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -3,7 +3,7 @@
  *
  * Based on git-am.sh by Junio C Hamano.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -1001,7 +1001,8 @@
 
 	if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
 		die_errno(_("failed to create directory '%s'"), state->dir);
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
 
 	if (split_mail(state, patch_format, paths, keep_cr) < 0) {
 		am_destroy(state);
@@ -1081,12 +1082,15 @@
 	if (!repo_get_oid(the_repository, "HEAD", &curr_head)) {
 		write_state_text(state, "abort-safety", oid_to_hex(&curr_head));
 		if (!state->rebasing)
-			update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0,
-				   UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					"am", "ORIG_HEAD", &curr_head, NULL,
+					0,
+					UPDATE_REFS_DIE_ON_ERR);
 	} else {
 		write_state_text(state, "abort-safety", "");
 		if (!state->rebasing)
-			delete_ref(NULL, "ORIG_HEAD", NULL, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, "ORIG_HEAD", NULL, 0);
 	}
 
 	/*
@@ -1119,7 +1123,8 @@
 
 	oidclr(&state->orig_commit);
 	unlink(am_path(state, "original-commit"));
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
 
 	if (!repo_get_oid(the_repository, "HEAD", &head))
 		write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1466,8 +1471,9 @@
 
 	oidcpy(&state->orig_commit, &commit_oid);
 	write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
-	update_ref("am", "REBASE_HEAD", &commit_oid,
-		   NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), "am",
+			"REBASE_HEAD", &commit_oid,
+			NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
 
 	return 0;
 }
@@ -1536,8 +1542,8 @@
 
 	if (index_file) {
 		/* Reload index as apply_all_patches() will have modified it. */
-		discard_index(&the_index);
-		read_index_from(&the_index, index_file, get_git_dir());
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, index_file, get_git_dir());
 	}
 
 	return 0;
@@ -1579,10 +1585,10 @@
 	if (build_fake_ancestor(state, index_path))
 		return error("could not build fake ancestor");
 
-	discard_index(&the_index);
-	read_index_from(&the_index, index_path, get_git_dir());
+	discard_index(the_repository->index);
+	read_index_from(the_repository->index, index_path, get_git_dir());
 
-	if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&orig_tree, the_repository->index, index_path, 0, NULL))
 		return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
 
 	say(state, stdout, _("Using index info to reconstruct a base tree..."));
@@ -1608,12 +1614,12 @@
 		return error(_("Did you hand edit your patch?\n"
 				"It does not apply to blobs recorded in its index."));
 
-	if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL))
+	if (write_index_as_tree(&their_tree, the_repository->index, index_path, 0, NULL))
 		return error("could not write tree");
 
 	say(state, stdout, _("Falling back to patching base and 3-way merge..."));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
 
 	/*
@@ -1660,7 +1666,7 @@
 	if (!state->no_verify && run_hooks("pre-applypatch"))
 		exit(1);
 
-	if (write_index_as_tree(&tree, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&tree, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 
 	if (!repo_get_oid_commit(the_repository, "HEAD", &parent)) {
@@ -1697,8 +1703,9 @@
 	strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg),
 			state->msg);
 
-	update_ref(sb.buf, "HEAD", &commit, old_oid, 0,
-		   UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), sb.buf, "HEAD",
+			&commit, old_oid, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (state->rebasing) {
 		FILE *fp = xfopen(am_path(state, "rewritten"), "a");
@@ -1948,7 +1955,7 @@
 		}
 	}
 
-	if (unmerged_index(&the_index)) {
+	if (unmerged_index(the_repository->index)) {
 		printf_ln(_("You still have unmerged paths in your index.\n"
 			"You should 'git add' each file with resolved conflicts to mark them as such.\n"
 			"You might run `git rm` on a file to accept \"deleted by them\" for it."));
@@ -1987,12 +1994,12 @@
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
 
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
@@ -2006,7 +2013,7 @@
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2029,8 +2036,8 @@
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	init_tree_desc(&t[0], &tree->object.oid, tree->buffer, tree->size);
@@ -2040,7 +2047,7 @@
 		return -1;
 	}
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	return 0;
@@ -2068,7 +2075,7 @@
 	if (fast_forward_to(head_tree, head_tree, 1))
 		return -1;
 
-	if (write_index_as_tree(&index, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(&index, the_repository->index, get_index_file(), 0, NULL))
 		return -1;
 
 	index_tree = parse_tree_indirect(&index);
@@ -2175,7 +2182,8 @@
 
 	am_rerere_clear();
 
-	curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL);
+	curr_branch = refs_resolve_refdup(get_main_ref_store(the_repository),
+					  "HEAD", 0, &curr_head, NULL);
 	has_curr_head = curr_branch && !is_null_oid(&curr_head);
 	if (!has_curr_head)
 		oidcpy(&curr_head, the_hash_algo->empty_tree);
@@ -2188,11 +2196,13 @@
 		die(_("failed to clean index"));
 
 	if (has_orig_head)
-		update_ref("am --abort", "HEAD", &orig_head,
-			   has_curr_head ? &curr_head : NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"am --abort", "HEAD", &orig_head,
+				has_curr_head ? &curr_head : NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	else if (curr_branch)
-		delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF);
+		refs_delete_ref(get_main_ref_store(the_repository), NULL,
+				curr_branch, NULL, REF_NO_DEREF);
 
 	free(curr_branch);
 	am_destroy(state);
diff --git a/builtin/apply.c b/builtin/apply.c
index 861a019..d623c52 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,6 +1,7 @@
 #include "builtin.h"
 #include "gettext.h"
 #include "repository.h"
+#include "hash.h"
 #include "apply.h"
 
 static const char * const apply_usage[] = {
@@ -18,6 +19,15 @@
 	if (init_apply_state(&state, the_repository, prefix))
 		exit(128);
 
+	/*
+	 * We could to redo the "apply.c" machinery to make this
+	 * arbitrary fallback unnecessary, but it is dubious that it
+	 * is worth the effort.
+	 * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/
+	 */
+	if (!the_hash_algo)
+		repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
 	argc = apply_parse_options(argc, argv,
 				   &state, &force_apply, &options,
 				   apply_usage);
diff --git a/builtin/bisect.c b/builtin/bisect.c
index f69c3f7..a58432b 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -243,7 +243,7 @@
 		strbuf_addstr(&branch, commit);
 	}
 
-	if (branch.len && !ref_exists("BISECT_HEAD")) {
+	if (branch.len && !refs_ref_exists(get_main_ref_store(the_repository), "BISECT_HEAD")) {
 		struct child_process cmd = CHILD_PROCESS_INIT;
 
 		cmd.git_cmd = 1;
@@ -302,8 +302,8 @@
 		goto finish;
 	}
 
-	if (update_ref(NULL, tag.buf, &oid, NULL, 0,
-		       UPDATE_REFS_MSG_ON_ERR)) {
+	if (refs_update_ref(get_main_ref_store(the_repository), NULL, tag.buf, &oid, NULL, 0,
+			    UPDATE_REFS_MSG_ON_ERR)) {
 		res = -1;
 		goto finish;
 	}
@@ -416,11 +416,12 @@
 	char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
 	char *good_glob = xstrfmt("%s-*", terms->term_good);
 
-	if (ref_exists(bad_ref))
+	if (refs_ref_exists(get_main_ref_store(the_repository), bad_ref))
 		state->nr_bad = 1;
 
-	for_each_glob_ref_in(inc_nr, good_glob, "refs/bisect/",
-			     (void *) &state->nr_good);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository), inc_nr,
+				  good_glob, "refs/bisect/",
+				  (void *) &state->nr_good);
 
 	free(good_glob);
 	free(bad_ref);
@@ -574,9 +575,11 @@
 	reset_revision_walk();
 	repo_init_revisions(the_repository, revs, NULL);
 	setup_revisions(0, NULL, revs, NULL);
-	for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  add_bisect_ref, bad, "refs/bisect/", &cb);
 	cb.object_flags = UNINTERESTING;
-	for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  add_bisect_ref, good, "refs/bisect/", &cb);
 	if (prepare_revision_walk(revs))
 		res = error(_("revision walk setup failed\n"));
 
@@ -636,7 +639,7 @@
 	char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad);
 	int res;
 
-	read_ref(bad_ref, &oid);
+	refs_read_ref(get_main_ref_store(the_repository), bad_ref, &oid);
 	commit = lookup_commit_reference_by_name(bad_ref);
 	repo_format_commit_message(the_repository, commit, "%s", &commit_name,
 				   &pp);
@@ -779,7 +782,8 @@
 	/*
 	 * Verify HEAD
 	 */
-	head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags);
+	head = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+				       "HEAD", 0, &head_oid, &flags);
 	if (!head)
 		if (repo_get_oid(the_repository, "HEAD", &head_oid))
 			return error(_("bad HEAD - I need a HEAD"));
@@ -838,8 +842,8 @@
 			res = error(_("invalid ref: '%s'"), start_head.buf);
 			goto finish;
 		}
-		if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
-			       UPDATE_REFS_MSG_ON_ERR)) {
+		if (refs_update_ref(get_main_ref_store(the_repository), NULL, "BISECT_HEAD", &oid, NULL, 0,
+				    UPDATE_REFS_MSG_ON_ERR)) {
 			res = BISECT_FAILED;
 			goto finish;
 		}
@@ -972,7 +976,7 @@
 		oid_array_append(&revs, &commit->object.oid);
 	}
 
-	if (read_ref("BISECT_EXPECTED_REV", &expected))
+	if (refs_read_ref(get_main_ref_store(the_repository), "BISECT_EXPECTED_REV", &expected))
 		verify_expected = 0; /* Ignore invalid file contents */
 
 	for (i = 0; i < revs.nr; i++) {
@@ -982,7 +986,9 @@
 		}
 		if (verify_expected && !oideq(&revs.oid[i], &expected)) {
 			unlink_or_warn(git_path_bisect_ancestors_ok());
-			delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, "BISECT_EXPECTED_REV", NULL,
+					REF_NO_DEREF);
 			verify_expected = 0;
 		}
 	}
@@ -1179,13 +1185,15 @@
 	struct object_id good_rev;
 	struct object_id current_rev;
 	char *good_glob = xstrfmt("%s-*", terms->term_good);
-	int no_checkout = ref_exists("BISECT_HEAD");
+	int no_checkout = refs_ref_exists(get_main_ref_store(the_repository),
+					  "BISECT_HEAD");
 
-	for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
-			     &good_rev);
+	refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+				  get_first_good, good_glob, "refs/bisect/",
+				  &good_rev);
 	free(good_glob);
 
-	if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
+	if (refs_read_ref(get_main_ref_store(the_repository), no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
 		return -1;
 
 	res = bisect_checkout(&good_rev, no_checkout);
diff --git a/builtin/blame.c b/builtin/blame.c
index 9aa7468..e09ff01 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -718,7 +718,7 @@
 		return 0;
 	}
 	if (!strcmp(var, "blame.ignorerevsfile")) {
-		const char *str;
+		char *str;
 		int ret;
 
 		ret = git_config_pathname(&str, var, value);
@@ -915,7 +915,6 @@
 	struct range_set ranges;
 	unsigned int range_i;
 	long anchor;
-	const int hexsz = the_hash_algo->hexsz;
 	long num_lines = 0;
 	const char *str_usage = cmd_is_annotate ? annotate_usage : blame_usage;
 	const char **opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage;
@@ -973,11 +972,11 @@
 	} else if (show_progress < 0)
 		show_progress = isatty(2);
 
-	if (0 < abbrev && abbrev < hexsz)
+	if (0 < abbrev && abbrev < (int)the_hash_algo->hexsz)
 		/* one more abbrev length is needed for the boundary commit */
 		abbrev++;
 	else if (!abbrev)
-		abbrev = hexsz;
+		abbrev = the_hash_algo->hexsz;
 
 	if (revs_file && read_ancestry(revs_file))
 		die_errno("reading graft file '%s' failed", revs_file);
@@ -1093,8 +1092,8 @@
 		struct commit *head_commit;
 		struct object_id head_oid;
 
-		if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-					&head_oid, NULL) ||
+		if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING,
+					     &head_oid, NULL) ||
 		    !(head_commit = lookup_commit_reference_gently(revs.repo,
 							     &head_oid, 1)))
 			die("no such ref: HEAD");
diff --git a/builtin/branch.c b/builtin/branch.c
index dd3e3a7..48cac74 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -148,8 +148,8 @@
 
 		if (upstream &&
 		    (reference_name = reference_name_to_free =
-		     resolve_refdup(upstream, RESOLVE_REF_READING,
-				    &oid, NULL)) != NULL)
+		     refs_resolve_refdup(get_main_ref_store(the_repository), upstream, RESOLVE_REF_READING,
+					 &oid, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(the_repository,
 								&oid);
 	}
@@ -272,21 +272,24 @@
 			}
 		}
 
-		target = resolve_refdup(name,
-					RESOLVE_REF_READING
-					| RESOLVE_REF_NO_RECURSE
-					| RESOLVE_REF_ALLOW_BAD_NAME,
-					&oid, &flags);
+		target = refs_resolve_refdup(get_main_ref_store(the_repository),
+					     name,
+					     RESOLVE_REF_READING
+					     | RESOLVE_REF_NO_RECURSE
+					     | RESOLVE_REF_ALLOW_BAD_NAME,
+					     &oid, &flags);
 		if (!target) {
 			if (remote_branch) {
 				error(_("remote-tracking branch '%s' not found"), bname.buf);
 			} else {
 				char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
-				char *virtual_target = resolve_refdup(virtual_name,
-							RESOLVE_REF_READING
-							| RESOLVE_REF_NO_RECURSE
-							| RESOLVE_REF_ALLOW_BAD_NAME,
-							&oid, &flags);
+				char *virtual_target = refs_resolve_refdup(get_main_ref_store(the_repository),
+									   virtual_name,
+									   RESOLVE_REF_READING
+									   | RESOLVE_REF_NO_RECURSE
+									   | RESOLVE_REF_ALLOW_BAD_NAME,
+									   &oid,
+									   &flags);
 				FREE_AND_NULL(virtual_name);
 
 				if (virtual_target)
@@ -317,13 +320,13 @@
 		free(target);
 	}
 
-	if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+	if (refs_delete_refs(get_main_ref_store(the_repository), 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)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), name)) {
 			char *refname = name + branch_name_pos;
 			if (!quiet)
 				printf(remote_branch
@@ -499,7 +502,8 @@
 static void print_current_branch_name(void)
 {
 	int flags;
-	const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						      "HEAD", 0, NULL, &flags);
 	const char *shortname;
 	if (!refname)
 		die(_("could not resolve HEAD"));
@@ -555,7 +559,7 @@
 			continue;
 
 		refs = get_worktree_ref_store(worktrees[i]);
-		if (refs_create_symref(refs, "HEAD", newref, logmsg))
+		if (refs_update_symref(refs, "HEAD", newref, logmsg))
 			ret = error(_("HEAD of working tree %s is not updated"),
 				    worktrees[i]->path);
 	}
@@ -580,7 +584,7 @@
 		 * Bad name --- this could be an attempt to rename a
 		 * ref that we used to allow to be created by accident.
 		 */
-		if (ref_exists(oldref.buf))
+		if (refs_ref_exists(get_main_ref_store(the_repository), oldref.buf))
 			recovery = 1;
 		else {
 			int code = die_message(_("invalid branch name: '%s'"), oldname);
@@ -601,7 +605,7 @@
 		}
 	}
 
-	if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+	if ((copy || !(oldref_usage & IS_HEAD)) && !refs_ref_exists(get_main_ref_store(the_repository), oldref.buf)) {
 		if (oldref_usage & IS_HEAD)
 			die(_("no commit on branch '%s' yet"), oldname);
 		else
@@ -632,9 +636,9 @@
 			    oldref.buf, newref.buf);
 
 	if (!copy && !(oldref_usage & IS_ORPHAN) &&
-	    rename_ref(oldref.buf, newref.buf, logmsg.buf))
+	    refs_rename_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
 		die(_("branch rename failed"));
-	if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
+	if (copy && refs_copy_existing_ref(get_main_ref_store(the_repository), oldref.buf, newref.buf, logmsg.buf))
 		die(_("branch copy failed"));
 
 	if (recovery) {
@@ -786,7 +790,8 @@
 
 	track = git_branch_track;
 
-	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   0, &head_oid, NULL);
 	if (!head)
 		die(_("failed to resolve HEAD as a valid ref"));
 	if (!strcmp(head, "HEAD"))
@@ -891,7 +896,7 @@
 		}
 
 		strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
-		if (!ref_exists(branch_ref.buf))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch_ref.buf))
 			error((!argc || branch_checked_out(branch_ref.buf))
 			      ? _("no commit on branch '%s' yet")
 			      : _("no branch named '%s'"),
@@ -936,7 +941,7 @@
 			die(_("no such branch '%s'"), argv[0]);
 		}
 
-		if (!ref_exists(branch->refname)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname)) {
 			if (!argc || branch_checked_out(branch->refname))
 				die(_("no commit on branch '%s' yet"), branch->name);
 			die(_("branch '%s' does not exist"), branch->name);
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 3ad11dc..d5d41a8 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -140,6 +140,11 @@
 			builtin_bundle_verify_usage, options, &bundle_file);
 	/* bundle internals use argv[1] as further parameters */
 
+	if (!startup_info->have_repository) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
+
 	if ((bundle_fd = open_bundle(bundle_file, &header, &name)) < 0) {
 		ret = 1;
 		goto cleanup;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0c948f4..43a1d7a 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "convert.h"
@@ -77,7 +77,7 @@
 		struct checkout_metadata meta;
 
 		init_checkout_metadata(&meta, NULL, NULL, oid);
-		if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
+		if (convert_to_working_tree(the_repository->index, path, *buf, *size, &strbuf, &meta)) {
 			free(*buf);
 			*size = strbuf.len;
 			*buf = strbuf_detach(&strbuf, NULL);
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index c1da1d1..9376810 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "attr.h"
@@ -71,9 +70,9 @@
 		prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
 
 	if (collect_all) {
-		git_all_attrs(&the_index, full_path, check);
+		git_all_attrs(the_repository->index, full_path, check);
 	} else {
-		git_check_attr(&the_index, full_path, check);
+		git_check_attr(the_repository->index, full_path, check);
 	}
 	output_attr(check, file);
 
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 906cd96..6c43430 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "dir.h"
@@ -95,21 +94,21 @@
 		       PATHSPEC_KEEP_ORDER,
 		       prefix, argv);
 
-	die_path_inside_submodule(&the_index, &pathspec);
+	die_path_inside_submodule(the_repository->index, &pathspec);
 
 	/*
 	 * look for pathspecs matching entries in the index, since these
 	 * should not be ignored, in order to be consistent with
 	 * 'git status', 'git add' etc.
 	 */
-	seen = find_pathspecs_matching_against_index(&pathspec, &the_index,
+	seen = find_pathspecs_matching_against_index(&pathspec, the_repository->index,
 						     PS_HEED_SKIP_WORKTREE);
 	for (i = 0; i < pathspec.nr; i++) {
 		full_path = pathspec.items[i].match;
 		pattern = NULL;
 		if (!seen[i]) {
 			int dtype = DT_UNKNOWN;
-			pattern = last_matching_pattern(dir, &the_index,
+			pattern = last_matching_pattern(dir, the_repository->index,
 							full_path, &dtype);
 			if (!verbose && pattern &&
 			    pattern->flags & PATTERN_FLAG_NEGATIVE)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 2e086a2..29e744d 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -4,7 +4,7 @@
  * Copyright (C) 2005 Linus Torvalds
  *
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -69,7 +69,7 @@
 static int checkout_file(const char *name, const char *prefix)
 {
 	int namelen = strlen(name);
-	int pos = index_name_pos(&the_index, name, namelen);
+	int pos = index_name_pos(the_repository->index, name, namelen);
 	int has_same_name = 0;
 	int is_file = 0;
 	int is_skipped = 1;
@@ -79,8 +79,8 @@
 	if (pos < 0)
 		pos = -pos - 1;
 
-	while (pos < the_index.cache_nr) {
-		struct cache_entry *ce = the_index.cache[pos];
+	while (pos <the_repository->index->cache_nr) {
+		struct cache_entry *ce =the_repository->index->cache[pos];
 		if (ce_namelen(ce) != namelen ||
 		    memcmp(ce->name, name, namelen))
 			break;
@@ -140,8 +140,8 @@
 	int i, errs = 0;
 	struct cache_entry *last_ce = NULL;
 
-	for (i = 0; i < the_index.cache_nr ; i++) {
-		struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr ; i++) {
+		struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
 			if (!ce_skip_worktree(ce))
@@ -154,8 +154,8 @@
 			 * first entry inside the expanded sparse directory).
 			 */
 			if (ignore_skip_worktree) {
-				ensure_full_index(&the_index);
-				ce = the_index.cache[i];
+				ensure_full_index(the_repository->index);
+				ce = the_repository->index->cache[i];
 			}
 		}
 
@@ -260,7 +260,7 @@
 
 	argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
 			builtin_checkout_index_usage, 0);
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 	state.force = force;
 	state.quiet = quiet;
 	state.not_new = not_new;
@@ -280,7 +280,7 @@
 	 */
 	if (index_opt && !state.base_dir_len && !to_tempfile) {
 		state.refresh_cache = 1;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 		repo_hold_locked_index(the_repository, &lock_file,
 				       LOCK_DIE_ON_ERROR);
 	}
@@ -339,7 +339,7 @@
 		return 1;
 
 	if (is_lock_file_locked(&lock_file) &&
-	    write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	    write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("Unable to write new index file");
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 71e6036..3cf44b4 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "branch.h"
@@ -146,7 +145,7 @@
 		return READ_TREE_RECURSIVE;
 
 	len = base->len + strlen(pathname);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, base->buf, base->len);
 	memcpy(ce->name + base->len, pathname, len - base->len);
@@ -159,9 +158,9 @@
 	 * entry in place. Whether it is UPTODATE or not, checkout_entry will
 	 * do the right thing.
 	 */
-	pos = index_name_pos(&the_index, ce->name, ce->ce_namelen);
+	pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen);
 	if (pos >= 0) {
-		struct cache_entry *old = the_index.cache[pos];
+		struct cache_entry *old = the_repository->index->cache[pos];
 		if (ce->ce_mode == old->ce_mode &&
 		    !ce_intent_to_add(old) &&
 		    oideq(&ce->oid, &old->oid)) {
@@ -171,7 +170,7 @@
 		}
 	}
 
-	add_index_entry(&the_index, ce,
+	add_index_entry(the_repository->index, ce,
 			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	return 0;
 }
@@ -190,8 +189,8 @@
 
 static int skip_same_name(const struct cache_entry *ce, int pos)
 {
-	while (++pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name))
+	while (++pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name))
 		; /* skip */
 	return pos;
 }
@@ -199,9 +198,9 @@
 static int check_stage(int stage, const struct cache_entry *ce, int pos,
 		       int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
 			return 0;
 		pos++;
 	}
@@ -218,8 +217,8 @@
 	unsigned seen = 0;
 	const char *name = ce->name;
 
-	while (pos < the_index.cache_nr) {
-		ce = the_index.cache[pos];
+	while (pos < the_repository->index->cache_nr) {
+		ce = the_repository->index->cache[pos];
 		if (strcmp(name, ce->name))
 			break;
 		seen |= (1 << ce_stage(ce));
@@ -235,10 +234,10 @@
 			  const struct checkout *state, int *nr_checkouts,
 			  int overlay_mode)
 {
-	while (pos < the_index.cache_nr &&
-	       !strcmp(the_index.cache[pos]->name, ce->name)) {
-		if (ce_stage(the_index.cache[pos]) == stage)
-			return checkout_entry(the_index.cache[pos], state,
+	while (pos < the_repository->index->cache_nr &&
+	       !strcmp(the_repository->index->cache[pos]->name, ce->name)) {
+		if (ce_stage(the_repository->index->cache[pos]) == stage)
+			return checkout_entry(the_repository->index->cache[pos], state,
 					      NULL, nr_checkouts);
 		pos++;
 	}
@@ -256,7 +255,7 @@
 			   int *nr_checkouts, struct mem_pool *ce_mem_pool,
 			   int conflict_style)
 {
-	struct cache_entry *ce = the_index.cache[pos];
+	struct cache_entry *ce = the_repository->index->cache[pos];
 	const char *path = ce->name;
 	mmfile_t ancestor, ours, theirs;
 	enum ll_merge_result merge_status;
@@ -269,7 +268,7 @@
 	int renormalize = 0;
 
 	memset(threeway, 0, sizeof(threeway));
-	while (pos < the_index.cache_nr) {
+	while (pos < the_repository->index->cache_nr) {
 		int stage;
 		stage = ce_stage(ce);
 		if (!stage || strcmp(path, ce->name))
@@ -278,7 +277,7 @@
 		if (stage == 2)
 			mode = create_ce_mode(ce->ce_mode);
 		pos++;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 	}
 	if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
 		return error(_("path '%s' does not have necessary versions"), path);
@@ -356,7 +355,7 @@
 	 * match_pathspec() for _all_ entries when
 	 * opts->source_tree != NULL.
 	 */
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched))
 		ce->ce_flags |= CE_MATCHED;
 }
 
@@ -367,7 +366,7 @@
 	ce->ce_flags &= ~CE_MATCHED;
 	if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 		return;
-	if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
+	if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) {
 		ce->ce_flags |= CE_MATCHED;
 		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 			/*
@@ -391,7 +390,7 @@
 
 	state.force = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	mem_pool_init(&ce_mem_pool, 0);
 	get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -404,8 +403,8 @@
 	if (pc_workers > 1)
 		init_parallel_checkout();
 
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce)) {
 				errs |= checkout_entry(ce, &state,
@@ -429,7 +428,7 @@
 		errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
 					      NULL, NULL);
 	mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
-	remove_marked_cache_entries(&the_index, 1);
+	remove_marked_cache_entries(the_repository->index, 1);
 	remove_scheduled_dirs();
 	errs |= finish_delayed_checkout(&state, opts->show_progress);
 
@@ -571,7 +570,7 @@
 	if (opts->source_tree)
 		read_tree_some(opts->source_tree, &opts->pathspec);
 	if (opts->merge)
-		unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
+		unmerge_index(the_repository->index, &opts->pathspec, CE_MATCHED);
 
 	ps_matched = xcalloc(opts->pathspec.nr, 1);
 
@@ -579,13 +578,13 @@
 	 * Make sure all pathspecs participated in locating the paths
 	 * to be checked out.
 	 */
-	for (pos = 0; pos < the_index.cache_nr; pos++)
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++)
 		if (opts->overlay_mode)
-			mark_ce_for_checkout_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_overlay(the_repository->index->cache[pos],
 						     ps_matched,
 						     opts);
 		else
-			mark_ce_for_checkout_no_overlay(the_index.cache[pos],
+			mark_ce_for_checkout_no_overlay(the_repository->index->cache[pos],
 							ps_matched,
 							opts);
 
@@ -596,8 +595,8 @@
 	free(ps_matched);
 
 	/* Any unmerged paths? */
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (ce->ce_flags & CE_MATCHED) {
 			if (!ce_stage(ce))
 				continue;
@@ -622,7 +621,7 @@
 	if (opts->checkout_worktree)
 		errs |= checkout_worktree(opts, new_branch_info);
 	else
-		remove_marked_cache_entries(&the_index, 1);
+		remove_marked_cache_entries(the_repository->index, 1);
 
 	/*
 	 * Allow updating the index when checking out from the index.
@@ -634,7 +633,7 @@
 		checkout_index = opts->checkout_index;
 
 	if (checkout_index) {
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die(_("unable to write new index file"));
 	} else {
 		/*
@@ -646,7 +645,8 @@
 		rollback_lock_file(&lock_file);
 	}
 
-	read_ref_full("HEAD", 0, &rev, NULL);
+	refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0,
+			   &rev, NULL);
 	head = lookup_commit_reference_gently(the_repository, &rev, 1);
 
 	errs |= post_checkout_hook(head, head, 0);
@@ -703,8 +703,8 @@
 	opts.merge = 1;
 	opts.fn = oneway_merge;
 	opts.verbose_update = o->show_progress;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	init_checkout_metadata(&opts.meta, info->refname,
 			       info->commit ? &info->commit->object.oid : null_oid(),
 			       NULL);
@@ -756,12 +756,12 @@
 {
 	memset(topts, 0, sizeof(*topts));
 	topts->head_idx = -1;
-	topts->src_index = &the_index;
-	topts->dst_index = &the_index;
+	topts->src_index = the_repository->index;
+	topts->dst_index = the_repository->index;
 
 	setup_unpack_trees_porcelain(topts, "checkout");
 
-	topts->initial_checkout = is_index_unborn(&the_index);
+	topts->initial_checkout = is_index_unborn(the_repository->index);
 	topts->update = 1;
 	topts->merge = 1;
 	topts->quiet = merge && old_commit;
@@ -783,7 +783,7 @@
 	if (repo_read_index_preload(the_repository, NULL, 0) < 0)
 		return error(_("index file corrupt"));
 
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
 		if (new_branch_info->commit)
 			BUG("'switch --orphan' should never accept a commit as starting point");
@@ -807,9 +807,9 @@
 		struct unpack_trees_options topts;
 		const struct object_id *old_commit_oid;
 
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-		if (unmerged_index(&the_index)) {
+		if (unmerged_index(the_repository->index)) {
 			error(_("you need to resolve your current index first"));
 			return 1;
 		}
@@ -919,10 +919,10 @@
 		}
 	}
 
-	if (!cache_tree_fully_valid(the_index.cache_tree))
-		cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
+	if (!cache_tree_fully_valid(the_repository->index->cache_tree))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
@@ -958,7 +958,8 @@
 				int ret;
 				struct strbuf err = STRBUF_INIT;
 
-				ret = safe_create_reflog(refname, &err);
+				ret = refs_create_reflog(get_main_ref_store(the_repository),
+							 refname, &err);
 				if (ret) {
 					fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
 						opts->new_orphan_branch, err.buf);
@@ -999,8 +1000,10 @@
 	if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
 		/* Nothing to do. */
 	} else if (opts->force_detach || !new_branch_info->path) {	/* No longer on any branch. */
-		update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
-			   REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"HEAD", &new_branch_info->commit->object.oid,
+				NULL,
+				REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old_branch_info->path &&
 			    advice_enabled(ADVICE_DETACHED_HEAD) && !opts->force_detach)
@@ -1008,7 +1011,7 @@
 			describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
 		}
 	} else if (new_branch_info->path) {	/* Switch branches. */
-		if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
+		if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
 			die(_("unable to update HEAD"));
 		if (!opts->quiet) {
 			if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
@@ -1029,8 +1032,9 @@
 			}
 		}
 		if (old_branch_info->path && old_branch_info->name) {
-			if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
-				delete_reflog(old_branch_info->path);
+			if (!refs_ref_exists(get_main_ref_store(the_repository), old_branch_info->path) && refs_reflog_exists(get_main_ref_store(the_repository), old_branch_info->path))
+				refs_delete_reflog(get_main_ref_store(the_repository),
+						   old_branch_info->path);
 		}
 	}
 	remove_branch_state(the_repository, !opts->quiet);
@@ -1129,7 +1133,8 @@
 	object->flags &= ~UNINTERESTING;
 	add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
-	for_each_ref(add_pending_uninteresting_ref, &revs);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  add_pending_uninteresting_ref, &revs);
 	if (new_commit)
 		add_pending_oid(&revs, "HEAD",
 				&new_commit->object.oid,
@@ -1159,7 +1164,8 @@
 	trace2_cmd_mode("branch");
 
 	memset(&old_branch_info, 0, sizeof(old_branch_info));
-	old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
+	old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository),
+						   "HEAD", 0, &rev, &flag);
 	if (old_branch_info.path)
 		old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
 	if (!(flag & REF_ISSYMREF))
@@ -1247,7 +1253,7 @@
 	setup_branch_path(new_branch_info);
 
 	if (!check_refname_format(new_branch_info->path, 0) &&
-	    !read_ref(new_branch_info->path, &branch_rev))
+	    !refs_read_ref(get_main_ref_store(the_repository), new_branch_info->path, &branch_rev))
 		oidcpy(rev, &branch_rev);
 	else
 		/* not an existing branch */
@@ -1269,12 +1275,12 @@
 	}
 }
 
-static const char *parse_remote_branch(const char *arg,
-				       struct object_id *rev,
-				       int could_be_checkout_paths)
+static char *parse_remote_branch(const char *arg,
+				 struct object_id *rev,
+				 int could_be_checkout_paths)
 {
 	int num_matches = 0;
-	const char *remote = unique_tracking_name(arg, rev, &num_matches);
+	char *remote = unique_tracking_name(arg, rev, &num_matches);
 
 	if (remote && could_be_checkout_paths) {
 		die(_("'%s' could be both a local file and a tracking branch.\n"
@@ -1310,6 +1316,7 @@
 	const char **new_branch = &opts->new_branch;
 	int argcount = 0;
 	const char *arg;
+	char *remote = NULL;
 	int dash_dash_pos;
 	int has_dash_dash = 0;
 	int i;
@@ -1410,8 +1417,8 @@
 			recover_with_dwim = 0;
 
 		if (recover_with_dwim) {
-			const char *remote = parse_remote_branch(arg, rev,
-								 could_be_checkout_paths);
+			remote = parse_remote_branch(arg, rev,
+						     could_be_checkout_paths);
 			if (remote) {
 				*new_branch = arg;
 				arg = remote;
@@ -1453,6 +1460,7 @@
 		argc--;
 	}
 
+	free(remote);
 	return argcount;
 }
 
@@ -1466,7 +1474,8 @@
 	if (!opts->new_branch)
 		die(_("You are on a branch yet to be born"));
 	strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
-	status = create_symref("HEAD", branch_ref.buf, "checkout -b");
+	status = refs_update_symref(get_main_ref_store(the_repository),
+				    "HEAD", branch_ref.buf, "checkout -b");
 	strbuf_release(&branch_ref);
 	if (!opts->quiet)
 		fprintf(stderr, _("Switched to a new branch '%s'\n"),
@@ -1553,7 +1562,8 @@
 
 	if (opts->ignore_other_worktrees)
 		return;
-	head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+	head_ref = refs_resolve_refdup(get_main_ref_store(the_repository),
+				       "HEAD", 0, NULL, &flags);
 	if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
 		die_if_checked_out(full_ref, 1);
 	free(head_ref);
@@ -1634,7 +1644,7 @@
 		struct object_id rev;
 		int flag;
 
-		if (!read_ref_full("HEAD", 0, &rev, &flag) &&
+		if (!refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &rev, &flag) &&
 		    (flag & REF_ISSYMREF) && is_null_oid(&rev))
 			return switch_unborn_to_new_branch(opts);
 	}
diff --git a/builtin/clean.c b/builtin/clean.c
index 29efe84..ded5a91 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -6,7 +6,6 @@
  * Based on git-clean.sh by Pavel Roskin
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -714,7 +713,7 @@
 		for_each_string_list_item(item, &del_list) {
 			int dtype = DT_UNKNOWN;
 
-			if (is_excluded(&dir, &the_index, item->string, &dtype)) {
+			if (is_excluded(&dir, the_repository->index, item->string, &dtype)) {
 				*item->string = '\0';
 				changed++;
 			}
@@ -1021,7 +1020,7 @@
 		       PATHSPEC_PREFER_CWD,
 		       prefix, argv);
 
-	fill_directory(&dir, &the_index, &pathspec);
+	fill_directory(&dir, the_repository->index, &pathspec);
 	correct_untracked_entries(&dir);
 
 	for (i = 0; i < dir.nr; i++) {
@@ -1029,7 +1028,7 @@
 		struct stat st;
 		const char *rel;
 
-		if (!index_name_is_other(&the_index, ent->name, ent->len))
+		if (!index_name_is_other(the_repository->index, ent->name, ent->len))
 			continue;
 
 		if (lstat(ent->name, &st))
diff --git a/builtin/clone.c b/builtin/clone.c
index 74ec145..b89ca92 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -8,7 +8,6 @@
  * Clone a repository into a different directory that does not yet exist.
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -329,7 +328,20 @@
 	int src_len, dest_len;
 	struct dir_iterator *iter;
 	int iter_status;
-	struct strbuf realpath = STRBUF_INIT;
+
+	/*
+	 * Refuse copying directories by default which aren't owned by us. The
+	 * code that performs either the copying or hardlinking is not prepared
+	 * to handle various edge cases where an adversary may for example
+	 * racily swap out files for symlinks. This can cause us to
+	 * inadvertently use the wrong source file.
+	 *
+	 * Furthermore, even if we were prepared to handle such races safely,
+	 * creating hardlinks across user boundaries is an inherently unsafe
+	 * operation as the hardlinked files can be rewritten at will by the
+	 * potentially-untrusted user. We thus refuse to do so by default.
+	 */
+	die_upon_dubious_ownership(NULL, NULL, src_repo);
 
 	mkdir_if_missing(dest->buf, 0777);
 
@@ -377,9 +389,27 @@
 		if (unlink(dest->buf) && errno != ENOENT)
 			die_errno(_("failed to unlink '%s'"), dest->buf);
 		if (!option_no_hardlinks) {
-			strbuf_realpath(&realpath, src->buf, 1);
-			if (!link(realpath.buf, dest->buf))
+			if (!link(src->buf, dest->buf)) {
+				struct stat st;
+
+				/*
+				 * Sanity-check whether the created hardlink
+				 * actually links to the expected file now. This
+				 * catches time-of-check-time-of-use bugs in
+				 * case the source file was meanwhile swapped.
+				 */
+				if (lstat(dest->buf, &st))
+					die(_("hardlink cannot be checked at '%s'"), dest->buf);
+				if (st.st_mode != iter->st.st_mode ||
+				    st.st_ino != iter->st.st_ino ||
+				    st.st_dev != iter->st.st_dev ||
+				    st.st_size != iter->st.st_size ||
+				    st.st_uid != iter->st.st_uid ||
+				    st.st_gid != iter->st.st_gid)
+					die(_("hardlink different from source at '%s'"), dest->buf);
+
 				continue;
+			}
 			if (option_local > 0)
 				die_errno(_("failed to create link '%s'"), dest->buf);
 			option_no_hardlinks = 1;
@@ -392,8 +422,6 @@
 		strbuf_setlen(src, src_len);
 		die(_("failed to iterate over '%s'"), src->buf);
 	}
-
-	strbuf_release(&realpath);
 }
 
 static void clone_local(const char *src_repo, const char *dest_repo)
@@ -539,7 +567,8 @@
 	struct ref_transaction *t;
 	struct strbuf err = STRBUF_INIT;
 
-	t = ref_transaction_begin(&err);
+	t = ref_store_transaction_begin(get_main_ref_store(the_repository),
+					&err);
 	if (!t)
 		die("%s", err.buf);
 
@@ -570,8 +599,9 @@
 						     OBJECT_INFO_QUICK |
 						     OBJECT_INFO_SKIP_FETCH_OBJECT))
 			continue;
-		update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				ref->name, &ref->old_oid, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	}
 }
 
@@ -623,9 +653,9 @@
 		struct strbuf head_ref = STRBUF_INIT;
 		strbuf_addstr(&head_ref, branch_top);
 		strbuf_addstr(&head_ref, "HEAD");
-		if (create_symref(head_ref.buf,
-				  remote_head_points_at->peer_ref->name,
-				  msg) < 0)
+		if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf,
+				       remote_head_points_at->peer_ref->name,
+				       msg) < 0)
 			die(_("unable to update %s"), head_ref.buf);
 		strbuf_release(&head_ref);
 	}
@@ -637,33 +667,36 @@
 	const char *head;
 	if (our && skip_prefix(our->name, "refs/heads/", &head)) {
 		/* Local default branch link */
-		if (create_symref("HEAD", our->name, NULL) < 0)
+		if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
 			die(_("unable to update HEAD"));
 		if (!option_bare) {
-			update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
-				   UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					msg, "HEAD", &our->old_oid, NULL, 0,
+					UPDATE_REFS_DIE_ON_ERR);
 			install_branch_config(0, head, remote_name, our->name);
 		}
 	} else if (our) {
 		struct commit *c = lookup_commit_reference(the_repository,
 							   &our->old_oid);
 		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
-		update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				"HEAD", &c->object.oid, NULL, REF_NO_DEREF,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
 		 * HEAD points to a branch but we don't know which one.
 		 * Detach HEAD in all these cases.
 		 */
-		update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg,
+				"HEAD", &remote->old_oid, NULL, REF_NO_DEREF,
+				UPDATE_REFS_DIE_ON_ERR);
 	} else if (unborn && skip_prefix(unborn, "refs/heads/", &head)) {
 		/*
 		 * Unborn head from remote; same as "our" case above except
 		 * that we have no ref to update.
 		 */
-		if (create_symref("HEAD", unborn, NULL) < 0)
+		if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
 			die(_("unable to update HEAD"));
 		if (!option_bare)
 			install_branch_config(0, head, remote_name, unborn);
@@ -704,7 +737,8 @@
 	if (option_no_checkout)
 		return 0;
 
-	head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   RESOLVE_REF_READING, &oid, NULL);
 	if (!head) {
 		warning(_("remote HEAD refers to nonexistent ref, "
 			  "unable to checkout"));
@@ -731,8 +765,8 @@
 	opts.preserve_ignored = 0;
 	opts.fn = oneway_merge;
 	opts.verbose_update = (option_verbosity >= 0);
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	init_checkout_metadata(&opts.meta, head, &oid, NULL);
 
 	tree = parse_tree_indirect(&oid);
@@ -746,7 +780,7 @@
 
 	free(head);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die(_("unable to write new index file"));
 
 	err |= run_hooks_l("post-checkout", oid_to_hex(null_oid()),
@@ -936,7 +970,7 @@
 	int submodule_progress;
 	int filter_submodules = 0;
 	int hash_algo;
-	unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+	enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
 	const int do_not_override_repo_unix_permissions = -1;
 
 	struct transport_ls_refs_options transport_ls_refs_options =
@@ -1425,6 +1459,7 @@
 	} else if (remote_head) {
 		our_head_points_at = NULL;
 	} else {
+		char *to_free = NULL;
 		const char *branch;
 
 		if (!mapped_refs) {
@@ -1437,7 +1472,7 @@
 				"refs/heads/", &branch)) {
 			unborn_head  = xstrdup(transport_ls_refs_options.unborn_head_target);
 		} else {
-			branch = git_default_branch_name(0);
+			branch = to_free = repo_default_branch_name(the_repository, 0);
 			unborn_head = xstrfmt("refs/heads/%s", branch);
 		}
 
@@ -1453,6 +1488,8 @@
 		 * a match.
 		 */
 		our_head_points_at = find_remote_branch(mapped_refs, branch);
+
+		free(to_free);
 	}
 
 	write_refspec_config(src_ref_prefix, our_head_points_at,
@@ -1507,6 +1544,7 @@
 	free(dir);
 	free(path);
 	free(repo_to_free);
+	UNLEAK(repo);
 	junk_mode = JUNK_LEAVE_ALL;
 
 	transport_ls_refs_options_release(&transport_ls_refs_options);
diff --git a/builtin/commit.c b/builtin/commit.c
index 6e14844..f53e7e8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -5,7 +5,6 @@
  * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -38,6 +37,7 @@
 #include "commit-reach.h"
 #include "commit-graph.h"
 #include "pretty.h"
+#include "trailer.h"
 
 static const char * const builtin_commit_usage[] = {
 	N_("git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
@@ -107,7 +107,7 @@
 } commit_style;
 
 static const char *logfile, *force_author;
-static const char *template_file;
+static char *template_file;
 /*
  * The _message variables are commit names from which to take
  * the commit message and/or authorship.
@@ -133,7 +133,7 @@
  * is specified explicitly.
  */
 static enum commit_msg_cleanup_mode cleanup_mode;
-static const char *cleanup_arg;
+static char *cleanup_arg;
 
 static enum commit_whence whence;
 static int use_editor = 1, include_status = 1;
@@ -142,14 +142,6 @@
 
 static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
 
-static int opt_pass_trailer(const struct option *opt, const char *arg, int unset)
-{
-	BUG_ON_OPT_NEG(unset);
-
-	strvec_pushl(opt->value, "--trailer", arg, NULL);
-	return 0;
-}
-
 static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
 {
 	enum wt_status_format *value = (enum wt_status_format *)opt->value;
@@ -266,19 +258,19 @@
 
 	if (with_tree) {
 		char *max_prefix = common_prefix(pattern);
-		overlay_tree_on_index(&the_index, with_tree, max_prefix);
+		overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
 		free(max_prefix);
 	}
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		struct string_list_item *item;
 
 		if (ce->ce_flags & CE_UPDATE)
 			continue;
-		if (!ce_path_match(&the_index, ce, pattern, m))
+		if (!ce_path_match(the_repository->index, ce, pattern, m))
 			continue;
 		item = string_list_insert(list, ce->name);
 		if (ce_skip_worktree(ce))
@@ -302,10 +294,10 @@
 			continue;
 
 		if (!lstat(p->string, &st)) {
-			if (add_to_index(&the_index, p->string, &st, 0))
+			if (add_to_index(the_repository->index, p->string, &st, 0))
 				die(_("updating files failed"));
 		} else
-			remove_file_from_index(&the_index, p->string);
+			remove_file_from_index(the_repository->index, p->string);
 	}
 }
 
@@ -316,7 +308,7 @@
 	struct tree_desc t;
 
 	if (!current_head) {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		return;
 	}
 
@@ -324,8 +316,8 @@
 	opts.head_idx = 1;
 	opts.index_only = 1;
 	opts.merge = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	opts.fn = oneway_merge;
 	tree = parse_tree_indirect(&current_head->object.oid);
@@ -344,7 +336,7 @@
 	 * refresh_flags contains REFRESH_QUIET, so the only errors
 	 * are for unmerged entries.
 	 */
-	if (refresh_index(&the_index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, refresh_flags | REFRESH_IN_PORCELAIN, NULL, NULL, NULL))
 		die_resolve_conflict("commit");
 }
 
@@ -393,7 +385,7 @@
 
 		refresh_cache_or_die(refresh_flags);
 
-		if (write_locked_index(&the_index, &index_lock, 0))
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to create temporary index"));
 
 		old_repo_index_file = the_repository->index_file;
@@ -412,13 +404,13 @@
 			unsetenv(INDEX_ENVIRONMENT);
 		FREE_AND_NULL(old_index_env);
 
-		discard_index(&the_index);
-		read_index_from(&the_index, get_lock_file_path(&index_lock),
+		discard_index(the_repository->index);
+		read_index_from(the_repository->index, get_lock_file_path(&index_lock),
 				get_git_dir());
-		if (cache_tree_update(&the_index, WRITE_TREE_SILENT) == 0) {
+		if (cache_tree_update(the_repository->index, WRITE_TREE_SILENT) == 0) {
 			if (reopen_lock_file(&index_lock) < 0)
 				die(_("unable to write index file"));
-			if (write_locked_index(&the_index, &index_lock, 0))
+			if (write_locked_index(the_repository->index, &index_lock, 0))
 				die(_("unable to update temporary index"));
 		} else
 			warning(_("Failed to update main cache tree"));
@@ -450,8 +442,8 @@
 			exit(128);
 
 		refresh_cache_or_die(refresh_flags);
-		cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock, 0))
+		cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock, 0))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_NORMAL;
 		ret = get_lock_file_path(&index_lock);
@@ -472,10 +464,10 @@
 		repo_hold_locked_index(the_repository, &index_lock,
 				       LOCK_DIE_ON_ERROR);
 		refresh_cache_or_die(refresh_flags);
-		if (the_index.cache_changed
-		    || !cache_tree_fully_valid(the_index.cache_tree))
-			cache_tree_update(&the_index, WRITE_TREE_SILENT);
-		if (write_locked_index(&the_index, &index_lock,
+		if (the_repository->index->cache_changed
+		    || !cache_tree_fully_valid(the_repository->index->cache_tree))
+			cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+		if (write_locked_index(the_repository->index, &index_lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write new index file"));
 		commit_style = COMMIT_AS_IS;
@@ -516,15 +508,15 @@
 	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
 		exit(1);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("cannot read the index"));
 
 	repo_hold_locked_index(the_repository, &index_lock, LOCK_DIE_ON_ERROR);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
-	cache_tree_update(&the_index, WRITE_TREE_SILENT);
-	if (write_locked_index(&the_index, &index_lock, 0))
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+	cache_tree_update(the_repository->index, WRITE_TREE_SILENT);
+	if (write_locked_index(the_repository->index, &index_lock, 0))
 		die(_("unable to write new index file"));
 
 	hold_lock_file_for_update(&false_lock,
@@ -534,14 +526,14 @@
 
 	create_base_index(current_head);
 	add_remove_files(&partial);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 
-	if (write_locked_index(&the_index, &false_lock, 0))
+	if (write_locked_index(the_repository->index, &false_lock, 0))
 		die(_("unable to write temporary index file"));
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	ret = get_lock_file_path(&false_lock);
-	read_index_from(&the_index, ret, get_git_dir());
+	read_index_from(the_repository->index, ret, get_git_dir());
 out:
 	string_list_clear(&partial, 0);
 	clear_pathspec(&pathspec);
@@ -999,7 +991,7 @@
 		struct object_id oid;
 		const char *parent = "HEAD";
 
-		if (!the_index.initialized && repo_read_index(the_repository) < 0)
+		if (!the_repository->index->initialized && repo_read_index(the_repository) < 0)
 			die(_("Cannot read index"));
 
 		if (amend)
@@ -1009,11 +1001,11 @@
 			int i, ita_nr = 0;
 
 			/* TODO: audit for interaction with sparse-index. */
-			ensure_full_index(&the_index);
-			for (i = 0; i < the_index.cache_nr; i++)
-				if (ce_intent_to_add(the_index.cache[i]))
+			ensure_full_index(the_repository->index);
+			for (i = 0; i < the_repository->index->cache_nr; i++)
+				if (ce_intent_to_add(the_repository->index->cache[i]))
 					ita_nr++;
-			committable = the_index.cache_nr - ita_nr > 0;
+			committable = the_repository->index->cache_nr - ita_nr > 0;
 		} else {
 			/*
 			 * Unless the user did explicitly request a submodule
@@ -1038,14 +1030,7 @@
 	fclose(s->fp);
 
 	if (trailer_args.nr) {
-		struct child_process run_trailer = CHILD_PROCESS_INIT;
-
-		strvec_pushl(&run_trailer.args, "interpret-trailers",
-			     "--in-place", "--no-divider",
-			     git_path_commit_editmsg(), NULL);
-		strvec_pushv(&run_trailer.args, trailer_args.v);
-		run_trailer.git_cmd = 1;
-		if (run_command(&run_trailer))
+		if (amend_file_with_trailers(git_path_commit_editmsg(), &trailer_args))
 			die(_("unable to pass trailers to --trailers"));
 		strvec_clear(&trailer_args);
 	}
@@ -1081,11 +1066,11 @@
 		 * and could have updated it. We must do this before we invoke
 		 * the editor and after we invoke run_status above.
 		 */
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 
-	if (cache_tree_update(&the_index, 0)) {
+	if (cache_tree_update(the_repository->index, 0)) {
 		error(_("Error building trees"));
 		return 0;
 	}
@@ -1586,7 +1571,7 @@
 	    status_format != STATUS_FORMAT_PORCELAIN_V2)
 		progress_flag = REFRESH_PROGRESS;
 	repo_read_index(the_repository);
-	refresh_index(&the_index,
+	refresh_index(the_repository->index,
 		      REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
 		      &s.pathspec, NULL, NULL);
 
@@ -1673,7 +1658,7 @@
 		OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
 		OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
 		OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
-		OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+		OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG),
 		OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
 		OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
@@ -1856,7 +1841,7 @@
 		append_merge_tag_headers(parents, &tail);
 	}
 
-	if (commit_tree_extended(sb.buf, sb.len, &the_index.cache_tree->oid,
+	if (commit_tree_extended(sb.buf, sb.len, &the_repository->index->cache_tree->oid,
 				 parents, &oid, author_ident.buf, NULL,
 				 sign_commit, extra)) {
 		rollback_index_files();
diff --git a/builtin/config.c b/builtin/config.c
index 0015620..20a0b64 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -16,59 +16,112 @@
 #include "worktree.h"
 
 static const char *const builtin_config_usage[] = {
-	N_("git config [<options>]"),
+	N_("git config list [<file-option>] [<display-option>] [--includes]"),
+	N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+	N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+	N_("git config remove-section [<file-option>] <name>"),
+	N_("git config edit [<file-option>]"),
+	N_("git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]"),
 	NULL
 };
 
-static char *key;
-static regex_t *key_regexp;
-static const char *value_pattern;
-static regex_t *regexp;
-static int show_keys;
-static int omit_values;
-static int use_key_regexp;
-static int do_all;
-static int do_not_match;
-static char delim = '=';
-static char key_delim = ' ';
-static char term = '\n';
+static const char *const builtin_config_list_usage[] = {
+	N_("git config list [<file-option>] [<display-option>] [--includes]"),
+	NULL
+};
 
-static int use_global_config, use_system_config, use_local_config;
-static int use_worktree_config;
-static struct git_config_source given_config_source;
-static int actions, type;
-static char *default_value;
-static int end_nul;
-static int respect_includes_opt = -1;
-static struct config_options config_options;
-static int show_origin;
-static int show_scope;
-static int fixed_value;
-static const char *comment;
+static const char *const builtin_config_get_usage[] = {
+	N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"),
+	NULL
+};
 
-#define ACTION_GET (1<<0)
-#define ACTION_GET_ALL (1<<1)
-#define ACTION_GET_REGEXP (1<<2)
-#define ACTION_REPLACE_ALL (1<<3)
-#define ACTION_ADD (1<<4)
-#define ACTION_UNSET (1<<5)
-#define ACTION_UNSET_ALL (1<<6)
-#define ACTION_RENAME_SECTION (1<<7)
-#define ACTION_REMOVE_SECTION (1<<8)
-#define ACTION_LIST (1<<9)
-#define ACTION_EDIT (1<<10)
-#define ACTION_SET (1<<11)
-#define ACTION_SET_ALL (1<<12)
-#define ACTION_GET_COLOR (1<<13)
-#define ACTION_GET_COLORBOOL (1<<14)
-#define ACTION_GET_URLMATCH (1<<15)
+static const char *const builtin_config_set_usage[] = {
+	N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	NULL
+};
 
-/*
- * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than
- * one line of output and which should therefore be paged.
- */
-#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \
-			ACTION_GET_REGEXP | ACTION_GET_URLMATCH)
+static const char *const builtin_config_unset_usage[] = {
+	N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> <value>"),
+	NULL
+};
+
+static const char *const builtin_config_rename_section_usage[] = {
+	N_("git config rename-section [<file-option>] <old-name> <new-name>"),
+	NULL
+};
+
+static const char *const builtin_config_remove_section_usage[] = {
+	N_("git config remove-section [<file-option>] <name>"),
+	NULL
+};
+
+static const char *const builtin_config_edit_usage[] = {
+	N_("git config edit [<file-option>]"),
+	NULL
+};
+
+#define CONFIG_LOCATION_OPTIONS(opts) \
+	OPT_GROUP(N_("Config file location")), \
+	OPT_BOOL(0, "global", &opts.use_global_config, N_("use global config file")), \
+	OPT_BOOL(0, "system", &opts.use_system_config, N_("use system config file")), \
+	OPT_BOOL(0, "local", &opts.use_local_config, N_("use repository config file")), \
+	OPT_BOOL(0, "worktree", &opts.use_worktree_config, N_("use per-worktree config file")), \
+	OPT_STRING('f', "file", &opts.source.file, N_("file"), N_("use given config file")), \
+	OPT_STRING(0, "blob", &opts.source.blob, N_("blob-id"), N_("read config from given blob object"))
+
+struct config_location_options {
+	struct git_config_source source;
+	struct config_options options;
+	char *file_to_free;
+	int use_global_config;
+	int use_system_config;
+	int use_local_config;
+	int use_worktree_config;
+	int respect_includes_opt;
+};
+#define CONFIG_LOCATION_OPTIONS_INIT { \
+	.respect_includes_opt = -1, \
+}
+
+#define CONFIG_TYPE_OPTIONS(type) \
+	OPT_GROUP(N_("Type")), \
+	OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type), \
+	OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), \
+	OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), \
+	OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), \
+	OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), \
+	OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), \
+	OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE)
+
+#define CONFIG_DISPLAY_OPTIONS(opts) \
+	OPT_GROUP(N_("Display options")), \
+	OPT_BOOL('z', "null", &opts.end_nul, N_("terminate values with NUL byte")), \
+	OPT_BOOL(0, "name-only", &opts.omit_values, N_("show variable names only")), \
+	OPT_BOOL(0, "show-origin", &opts.show_origin, N_("show origin of config (file, standard input, blob, command line)")), \
+	OPT_BOOL(0, "show-scope", &opts.show_scope, N_("show scope of config (worktree, local, global, system, command)")), \
+	OPT_BOOL(0, "show-names", &opts.show_keys, N_("show config keys in addition to their values")), \
+	CONFIG_TYPE_OPTIONS(opts.type)
+
+struct config_display_options {
+	int end_nul;
+	int omit_values;
+	int show_origin;
+	int show_scope;
+	int show_keys;
+	int type;
+	char *default_value;
+	/* Populated via `display_options_init()`. */
+	int term;
+	int delim;
+	int key_delim;
+};
+#define CONFIG_DISPLAY_OPTIONS_INIT { \
+	.term = '\n', \
+	.delim = '=', \
+	.key_delim = ' ', \
+}
 
 #define TYPE_BOOL		1
 #define TYPE_INT		2
@@ -82,8 +135,6 @@
 	{ OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
 	PARSE_OPT_NONEG, option_parse_type, (i) }
 
-static NORETURN void usage_builtin_config(void);
-
 static int option_parse_type(const struct option *opt, const char *arg,
 			     int unset)
 {
@@ -128,61 +179,13 @@
 		 * --type=int'.
 		 */
 		error(_("only one type at a time"));
-		usage_builtin_config();
+		exit(129);
 	}
 	*to_type = new_type;
 
 	return 0;
 }
 
-static struct option builtin_config_options[] = {
-	OPT_GROUP(N_("Config file location")),
-	OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
-	OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
-	OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
-	OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")),
-	OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")),
-	OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")),
-	OPT_GROUP(N_("Action")),
-	OPT_BIT(0, "get", &actions, N_("get value: name [value-pattern]"), ACTION_GET),
-	OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-pattern]"), ACTION_GET_ALL),
-	OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-pattern]"), ACTION_GET_REGEXP),
-	OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
-	OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value-pattern]"), ACTION_REPLACE_ALL),
-	OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
-	OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-pattern]"), ACTION_UNSET),
-	OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-pattern]"), ACTION_UNSET_ALL),
-	OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
-	OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
-	OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
-	OPT_BOOL(0, "fixed-value", &fixed_value, N_("use string equality when comparing values to 'value-pattern'")),
-	OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
-	OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
-	OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
-	OPT_GROUP(N_("Type")),
-	OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
-	OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
-	OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
-	OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
-	OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR),
-	OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH),
-	OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE),
-	OPT_GROUP(N_("Other")),
-	OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")),
-	OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
-	OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
-	OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
-	OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")),
-	OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")),
-	OPT_STRING(0, "comment", &comment, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
-	OPT_END(),
-};
-
-static NORETURN void usage_builtin_config(void)
-{
-	usage_with_options(builtin_config_usage, builtin_config_options);
-}
-
 static void check_argc(int argc, int min, int max)
 {
 	if (argc >= min && argc <= max)
@@ -192,27 +195,29 @@
 	else
 		error(_("wrong number of arguments, should be from %d to %d"),
 		      min, max);
-	usage_builtin_config();
+	exit(129);
 }
 
-static void show_config_origin(const struct key_value_info *kvi,
+static void show_config_origin(const struct config_display_options *opts,
+			       const struct key_value_info *kvi,
 			       struct strbuf *buf)
 {
-	const char term = end_nul ? '\0' : '\t';
+	const char term = opts->end_nul ? '\0' : '\t';
 
 	strbuf_addstr(buf, config_origin_type_name(kvi->origin_type));
 	strbuf_addch(buf, ':');
-	if (end_nul)
+	if (opts->end_nul)
 		strbuf_addstr(buf, kvi->filename ? kvi->filename : "");
 	else
 		quote_c_style(kvi->filename ? kvi->filename : "", buf, NULL, 0);
 	strbuf_addch(buf, term);
 }
 
-static void show_config_scope(const struct key_value_info *kvi,
+static void show_config_scope(const struct config_display_options *opts,
+			      const struct key_value_info *kvi,
 			      struct strbuf *buf)
 {
-	const char term = end_nul ? '\0' : '\t';
+	const char term = opts->end_nul ? '\0' : '\t';
 	const char *scope = config_scope_name(kvi->scope);
 
 	strbuf_addstr(buf, N_(scope));
@@ -221,24 +226,25 @@
 
 static int show_all_config(const char *key_, const char *value_,
 			   const struct config_context *ctx,
-			   void *cb UNUSED)
+			   void *cb)
 {
+	const struct config_display_options *opts = cb;
 	const struct key_value_info *kvi = ctx->kvi;
 
-	if (show_origin || show_scope) {
+	if (opts->show_origin || opts->show_scope) {
 		struct strbuf buf = STRBUF_INIT;
-		if (show_scope)
-			show_config_scope(kvi, &buf);
-		if (show_origin)
-			show_config_origin(kvi, &buf);
+		if (opts->show_scope)
+			show_config_scope(opts, kvi, &buf);
+		if (opts->show_origin)
+			show_config_origin(opts, kvi, &buf);
 		/* Use fwrite as "buf" can contain \0's if "end_null" is set. */
 		fwrite(buf.buf, 1, buf.len, stdout);
 		strbuf_release(&buf);
 	}
-	if (!omit_values && value_)
-		printf("%s%c%s%c", key_, delim, value_, term);
+	if (!opts->omit_values && value_)
+		printf("%s%c%s%c", key_, opts->delim, value_, opts->term);
 	else
-		printf("%s%c", key_, term);
+		printf("%s%c", key_, opts->term);
 	return 0;
 }
 
@@ -248,26 +254,27 @@
 	int alloc;
 };
 
-static int format_config(struct strbuf *buf, const char *key_,
+static int format_config(const struct config_display_options *opts,
+			 struct strbuf *buf, const char *key_,
 			 const char *value_, const struct key_value_info *kvi)
 {
-	if (show_scope)
-		show_config_scope(kvi, buf);
-	if (show_origin)
-		show_config_origin(kvi, buf);
-	if (show_keys)
+	if (opts->show_scope)
+		show_config_scope(opts, kvi, buf);
+	if (opts->show_origin)
+		show_config_origin(opts, kvi, buf);
+	if (opts->show_keys)
 		strbuf_addstr(buf, key_);
-	if (!omit_values) {
-		if (show_keys)
-			strbuf_addch(buf, key_delim);
+	if (!opts->omit_values) {
+		if (opts->show_keys)
+			strbuf_addch(buf, opts->key_delim);
 
-		if (type == TYPE_INT)
+		if (opts->type == TYPE_INT)
 			strbuf_addf(buf, "%"PRId64,
 				    git_config_int64(key_, value_ ? value_ : "", kvi));
-		else if (type == TYPE_BOOL)
+		else if (opts->type == TYPE_BOOL)
 			strbuf_addstr(buf, git_config_bool(key_, value_) ?
 				      "true" : "false");
-		else if (type == TYPE_BOOL_OR_INT) {
+		else if (opts->type == TYPE_BOOL_OR_INT) {
 			int is_bool, v;
 			v = git_config_bool_or_int(key_, value_, kvi,
 						   &is_bool);
@@ -275,24 +282,24 @@
 				strbuf_addstr(buf, v ? "true" : "false");
 			else
 				strbuf_addf(buf, "%d", v);
-		} else if (type == TYPE_BOOL_OR_STR) {
+		} else if (opts->type == TYPE_BOOL_OR_STR) {
 			int v = git_parse_maybe_bool(value_);
 			if (v < 0)
 				strbuf_addstr(buf, value_);
 			else
 				strbuf_addstr(buf, v ? "true" : "false");
-		} else if (type == TYPE_PATH) {
-			const char *v;
+		} else if (opts->type == TYPE_PATH) {
+			char *v;
 			if (git_config_pathname(&v, key_, value_) < 0)
 				return -1;
 			strbuf_addstr(buf, v);
 			free((char *)v);
-		} else if (type == TYPE_EXPIRY_DATE) {
+		} else if (opts->type == TYPE_EXPIRY_DATE) {
 			timestamp_t t;
 			if (git_config_expiry_date(&t, key_, value_) < 0)
 				return -1;
 			strbuf_addf(buf, "%"PRItime, t);
-		} else if (type == TYPE_COLOR) {
+		} else if (opts->type == TYPE_COLOR) {
 			char v[COLOR_MAXLEN];
 			if (git_config_color(v, key_, value_) < 0)
 				return -1;
@@ -301,43 +308,73 @@
 			strbuf_addstr(buf, value_);
 		} else {
 			/* Just show the key name; back out delimiter */
-			if (show_keys)
+			if (opts->show_keys)
 				strbuf_setlen(buf, buf->len - 1);
 		}
 	}
-	strbuf_addch(buf, term);
+	strbuf_addch(buf, opts->term);
 	return 0;
 }
 
+#define GET_VALUE_ALL        (1 << 0)
+#define GET_VALUE_KEY_REGEXP (1 << 1)
+
+struct collect_config_data {
+	const struct config_display_options *display_opts;
+	struct strbuf_list *values;
+	const char *value_pattern;
+	const char *key;
+	regex_t *regexp;
+	regex_t *key_regexp;
+	int do_not_match;
+	unsigned get_value_flags;
+	unsigned flags;
+};
+
 static int collect_config(const char *key_, const char *value_,
 			  const struct config_context *ctx, void *cb)
 {
-	struct strbuf_list *values = cb;
+	struct collect_config_data *data = cb;
+	struct strbuf_list *values = data->values;
 	const struct key_value_info *kvi = ctx->kvi;
 
-	if (!use_key_regexp && strcmp(key_, key))
+	if (!(data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+	    strcmp(key_, data->key))
 		return 0;
-	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+	if ((data->get_value_flags & GET_VALUE_KEY_REGEXP) &&
+	    regexec(data->key_regexp, key_, 0, NULL, 0))
 		return 0;
-	if (fixed_value && strcmp(value_pattern, (value_?value_:"")))
+	if ((data->flags & CONFIG_FLAGS_FIXED_VALUE) &&
+	    strcmp(data->value_pattern, (value_?value_:"")))
 		return 0;
-	if (regexp != NULL &&
-	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+	if (data->regexp &&
+	    (data->do_not_match ^ !!regexec(data->regexp, (value_?value_:""), 0, NULL, 0)))
 		return 0;
 
 	ALLOC_GROW(values->items, values->nr + 1, values->alloc);
 	strbuf_init(&values->items[values->nr], 0);
 
-	return format_config(&values->items[values->nr++], key_, value_, kvi);
+	return format_config(data->display_opts, &values->items[values->nr++],
+			     key_, value_, kvi);
 }
 
-static int get_value(const char *key_, const char *regex_, unsigned flags)
+static int get_value(const struct config_location_options *opts,
+		     const struct config_display_options *display_opts,
+		     const char *key_, const char *regex_,
+		     unsigned get_value_flags, unsigned flags)
 {
 	int ret = CONFIG_GENERIC_ERROR;
 	struct strbuf_list values = {NULL};
+	struct collect_config_data data = {
+		.display_opts = display_opts,
+		.values = &values,
+		.get_value_flags = get_value_flags,
+		.flags = flags,
+	};
+	char *key = NULL;
 	int i;
 
-	if (use_key_regexp) {
+	if (get_value_flags & GET_VALUE_KEY_REGEXP) {
 		char *tl;
 
 		/*
@@ -354,10 +391,10 @@
 		for (tl = key; *tl && *tl != '.'; tl++)
 			*tl = tolower(*tl);
 
-		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
-		if (regcomp(key_regexp, key, REG_EXTENDED)) {
+		data.key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(data.key_regexp, key, REG_EXTENDED)) {
 			error(_("invalid key pattern: %s"), key_);
-			FREE_AND_NULL(key_regexp);
+			FREE_AND_NULL(data.key_regexp);
 			ret = CONFIG_INVALID_PATTERN;
 			goto free_strings;
 		}
@@ -366,30 +403,32 @@
 			ret = CONFIG_INVALID_KEY;
 			goto free_strings;
 		}
+
+		data.key = key;
 	}
 
 	if (regex_ && (flags & CONFIG_FLAGS_FIXED_VALUE))
-		value_pattern = regex_;
+		data.value_pattern = regex_;
 	else if (regex_) {
 		if (regex_[0] == '!') {
-			do_not_match = 1;
+			data.do_not_match = 1;
 			regex_++;
 		}
 
-		regexp = (regex_t*)xmalloc(sizeof(regex_t));
-		if (regcomp(regexp, regex_, REG_EXTENDED)) {
+		data.regexp = (regex_t*)xmalloc(sizeof(regex_t));
+		if (regcomp(data.regexp, regex_, REG_EXTENDED)) {
 			error(_("invalid pattern: %s"), regex_);
-			FREE_AND_NULL(regexp);
+			FREE_AND_NULL(data.regexp);
 			ret = CONFIG_INVALID_PATTERN;
 			goto free_strings;
 		}
 	}
 
-	config_with_options(collect_config, &values,
-			    &given_config_source, the_repository,
-			    &config_options);
+	config_with_options(collect_config, &data,
+			    &opts->source, the_repository,
+			    &opts->options);
 
-	if (!values.nr && default_value) {
+	if (!values.nr && display_opts->default_value) {
 		struct key_value_info kvi = KVI_INIT;
 		struct strbuf *item;
 
@@ -397,16 +436,17 @@
 		ALLOC_GROW(values.items, values.nr + 1, values.alloc);
 		item = &values.items[values.nr++];
 		strbuf_init(item, 0);
-		if (format_config(item, key_, default_value, &kvi) < 0)
+		if (format_config(display_opts, item, key_,
+				  display_opts->default_value, &kvi) < 0)
 			die(_("failed to format default config value: %s"),
-				default_value);
+			    display_opts->default_value);
 	}
 
 	ret = !values.nr;
 
 	for (i = 0; i < values.nr; i++) {
 		struct strbuf *buf = values.items + i;
-		if (do_all || i == values.nr - 1)
+		if ((get_value_flags & GET_VALUE_ALL) || i == values.nr - 1)
 			fwrite(buf->buf, 1, buf->len, stdout);
 		strbuf_release(buf);
 	}
@@ -414,20 +454,20 @@
 
 free_strings:
 	free(key);
-	if (key_regexp) {
-		regfree(key_regexp);
-		free(key_regexp);
+	if (data.key_regexp) {
+		regfree(data.key_regexp);
+		free(data.key_regexp);
 	}
-	if (regexp) {
-		regfree(regexp);
-		free(regexp);
+	if (data.regexp) {
+		regfree(data.regexp);
+		free(data.regexp);
 	}
 
 	return ret;
 }
 
 static char *normalize_value(const char *key, const char *value,
-			     struct key_value_info *kvi)
+			     int type, struct key_value_info *kvi)
 {
 	if (!value)
 		return NULL;
@@ -478,97 +518,113 @@
 	BUG("cannot normalize type %d", type);
 }
 
-static int get_color_found;
-static const char *get_color_slot;
-static const char *get_colorbool_slot;
-static char parsed_color[COLOR_MAXLEN];
+struct get_color_config_data {
+	int get_color_found;
+	const char *get_color_slot;
+	char parsed_color[COLOR_MAXLEN];
+};
 
 static int git_get_color_config(const char *var, const char *value,
 				const struct config_context *ctx UNUSED,
-				void *cb UNUSED)
+				void *cb)
 {
-	if (!strcmp(var, get_color_slot)) {
+	struct get_color_config_data *data = cb;
+
+	if (!strcmp(var, data->get_color_slot)) {
 		if (!value)
 			config_error_nonbool(var);
-		if (color_parse(value, parsed_color) < 0)
+		if (color_parse(value, data->parsed_color) < 0)
 			return -1;
-		get_color_found = 1;
+		data->get_color_found = 1;
 	}
 	return 0;
 }
 
-static void get_color(const char *var, const char *def_color)
+static void get_color(const struct config_location_options *opts,
+		      const char *var, const char *def_color)
 {
-	get_color_slot = var;
-	get_color_found = 0;
-	parsed_color[0] = '\0';
-	config_with_options(git_get_color_config, NULL,
-			    &given_config_source, the_repository,
-			    &config_options);
+	struct get_color_config_data data = {
+		.get_color_slot = var,
+		.parsed_color[0] = '\0',
+	};
 
-	if (!get_color_found && def_color) {
-		if (color_parse(def_color, parsed_color) < 0)
+	config_with_options(git_get_color_config, &data,
+			    &opts->source, the_repository,
+			    &opts->options);
+
+	if (!data.get_color_found && def_color) {
+		if (color_parse(def_color, data.parsed_color) < 0)
 			die(_("unable to parse default color value"));
 	}
 
-	fputs(parsed_color, stdout);
+	fputs(data.parsed_color, stdout);
 }
 
-static int get_colorbool_found;
-static int get_diff_color_found;
-static int get_color_ui_found;
+struct get_colorbool_config_data {
+	int get_colorbool_found;
+	int get_diff_color_found;
+	int get_color_ui_found;
+	const char *get_colorbool_slot;
+};
+
 static int git_get_colorbool_config(const char *var, const char *value,
 				    const struct config_context *ctx UNUSED,
-				    void *data UNUSED)
+				    void *cb)
 {
-	if (!strcmp(var, get_colorbool_slot))
-		get_colorbool_found = git_config_colorbool(var, value);
+	struct get_colorbool_config_data *data = cb;
+
+	if (!strcmp(var, data->get_colorbool_slot))
+		data->get_colorbool_found = git_config_colorbool(var, value);
 	else if (!strcmp(var, "diff.color"))
-		get_diff_color_found = git_config_colorbool(var, value);
+		data->get_diff_color_found = git_config_colorbool(var, value);
 	else if (!strcmp(var, "color.ui"))
-		get_color_ui_found = git_config_colorbool(var, value);
+		data->get_color_ui_found = git_config_colorbool(var, value);
 	return 0;
 }
 
-static int get_colorbool(const char *var, int print)
+static int get_colorbool(const struct config_location_options *opts,
+			 const char *var, int print)
 {
-	get_colorbool_slot = var;
-	get_colorbool_found = -1;
-	get_diff_color_found = -1;
-	get_color_ui_found = -1;
-	config_with_options(git_get_colorbool_config, NULL,
-			    &given_config_source, the_repository,
-			    &config_options);
+	struct get_colorbool_config_data data = {
+		.get_colorbool_slot = var,
+		.get_colorbool_found = -1,
+		.get_diff_color_found = -1,
+		.get_color_ui_found = -1,
+	};
 
-	if (get_colorbool_found < 0) {
-		if (!strcmp(get_colorbool_slot, "color.diff"))
-			get_colorbool_found = get_diff_color_found;
-		if (get_colorbool_found < 0)
-			get_colorbool_found = get_color_ui_found;
+	config_with_options(git_get_colorbool_config, &data,
+			    &opts->source, the_repository,
+			    &opts->options);
+
+	if (data.get_colorbool_found < 0) {
+		if (!strcmp(data.get_colorbool_slot, "color.diff"))
+			data.get_colorbool_found = data.get_diff_color_found;
+		if (data.get_colorbool_found < 0)
+			data.get_colorbool_found = data.get_color_ui_found;
 	}
 
-	if (get_colorbool_found < 0)
+	if (data.get_colorbool_found < 0)
 		/* default value if none found in config */
-		get_colorbool_found = GIT_COLOR_AUTO;
+		data.get_colorbool_found = GIT_COLOR_AUTO;
 
-	get_colorbool_found = want_color(get_colorbool_found);
+	data.get_colorbool_found = want_color(data.get_colorbool_found);
 
 	if (print) {
-		printf("%s\n", get_colorbool_found ? "true" : "false");
+		printf("%s\n", data.get_colorbool_found ? "true" : "false");
 		return 0;
 	} else
-		return get_colorbool_found ? 0 : 1;
+		return data.get_colorbool_found ? 0 : 1;
 }
 
-static void check_write(void)
+static void check_write(const struct git_config_source *source)
 {
-	if (!given_config_source.file && !startup_info->have_repository)
+	if (!source->file && !startup_info->have_repository)
 		die(_("not in a git directory"));
 
-	if (given_config_source.use_stdin)
+	if (source->use_stdin)
 		die(_("writing to stdin is not supported"));
 
-	if (given_config_source.blob)
+	if (source->blob)
 		die(_("writing config blobs is not supported"));
 }
 
@@ -605,10 +661,13 @@
 	return 0;
 }
 
-static int get_urlmatch(const char *var, const char *url)
+static int get_urlmatch(const struct config_location_options *opts,
+			const struct config_display_options *_display_opts,
+			const char *var, const char *url)
 {
 	int ret;
 	char *section_tail;
+	struct config_display_options display_opts = *_display_opts;
 	struct string_list_item *item;
 	struct urlmatch_config config = URLMATCH_CONFIG_INIT;
 	struct string_list values = STRING_LIST_INIT_DUP;
@@ -625,15 +684,15 @@
 	if (section_tail) {
 		*section_tail = '\0';
 		config.key = section_tail + 1;
-		show_keys = 0;
+		display_opts.show_keys = 0;
 	} else {
 		config.key = NULL;
-		show_keys = 1;
+		display_opts.show_keys = 1;
 	}
 
 	config_with_options(urlmatch_config_entry, &config,
-			    &given_config_source, the_repository,
-			    &config_options);
+			    &opts->source, the_repository,
+			    &opts->options);
 
 	ret = !values.nr;
 
@@ -641,7 +700,7 @@
 		struct urlmatch_current_candidate_value *matched = item->util;
 		struct strbuf buf = STRBUF_INIT;
 
-		format_config(&buf, item->string,
+		format_config(&display_opts, &buf, item->string,
 			      matched->value_is_null ? NULL : matched->value.buf,
 			      &matched->kvi);
 		fwrite(buf.buf, 1, buf.len, stdout);
@@ -671,47 +730,39 @@
 	return strbuf_detach(&buf, NULL);
 }
 
-int cmd_config(int argc, const char **argv, const char *prefix)
+static void location_options_init(struct config_location_options *opts,
+				  const char *prefix)
 {
-	int nongit = !startup_info->have_repository;
-	char *value = NULL;
-	int flags = 0;
-	int ret = 0;
-	struct key_value_info default_kvi = KVI_INIT;
+	if (!opts->source.file)
+		opts->source.file = opts->file_to_free =
+			xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
 
-	given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT));
-
-	argc = parse_options(argc, argv, prefix, builtin_config_options,
-			     builtin_config_usage,
-			     PARSE_OPT_STOP_AT_NON_OPTION);
-
-	if (use_global_config + use_system_config + use_local_config +
-	    use_worktree_config +
-	    !!given_config_source.file + !!given_config_source.blob > 1) {
+	if (opts->use_global_config + opts->use_system_config +
+	    opts->use_local_config + opts->use_worktree_config +
+	    !!opts->source.file + !!opts->source.blob > 1) {
 		error(_("only one config file at a time"));
-		usage_builtin_config();
+		exit(129);
 	}
 
-	if (nongit) {
-		if (use_local_config)
+	if (!startup_info->have_repository) {
+		if (opts->use_local_config)
 			die(_("--local can only be used inside a git repository"));
-		if (given_config_source.blob)
+		if (opts->source.blob)
 			die(_("--blob can only be used inside a git repository"));
-		if (use_worktree_config)
+		if (opts->use_worktree_config)
 			die(_("--worktree can only be used inside a git repository"));
-
 	}
 
-	if (given_config_source.file &&
-			!strcmp(given_config_source.file, "-")) {
-		given_config_source.file = NULL;
-		given_config_source.use_stdin = 1;
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
+	if (opts->source.file &&
+			!strcmp(opts->source.file, "-")) {
+		opts->source.file = NULL;
+		opts->source.use_stdin = 1;
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
 	}
 
-	if (use_global_config) {
-		given_config_source.file = git_global_config();
-		if (!given_config_source.file)
+	if (opts->use_global_config) {
+		opts->source.file = opts->file_to_free = git_global_config();
+		if (!opts->source.file)
 			/*
 			 * It is unknown if HOME/.gitconfig exists, so
 			 * we do not know if we should write to XDG
@@ -719,17 +770,18 @@
 			 * is set and points at a sane location.
 			 */
 			die(_("$HOME not set"));
-		given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-	} else if (use_system_config) {
-		given_config_source.file = git_system_config();
-		given_config_source.scope = CONFIG_SCOPE_SYSTEM;
-	} else if (use_local_config) {
-		given_config_source.file = git_pathdup("config");
-		given_config_source.scope = CONFIG_SCOPE_LOCAL;
-	} else if (use_worktree_config) {
+		opts->source.scope = CONFIG_SCOPE_GLOBAL;
+	} else if (opts->use_system_config) {
+		opts->source.file = opts->file_to_free = git_system_config();
+		opts->source.scope = CONFIG_SCOPE_SYSTEM;
+	} else if (opts->use_local_config) {
+		opts->source.file = opts->file_to_free = git_pathdup("config");
+		opts->source.scope = CONFIG_SCOPE_LOCAL;
+	} else if (opts->use_worktree_config) {
 		struct worktree **worktrees = get_worktrees();
 		if (the_repository->repository_format_worktree_config)
-			given_config_source.file = git_pathdup("config.worktree");
+			opts->source.file = opts->file_to_free =
+				git_pathdup("config.worktree");
 		else if (worktrees[0] && worktrees[1])
 			die(_("--worktree cannot be used with multiple "
 			      "working trees unless the config\n"
@@ -737,76 +789,439 @@
 			      "Please read \"CONFIGURATION FILE\"\n"
 			      "section in \"git help worktree\" for details"));
 		else
-			given_config_source.file = git_pathdup("config");
-		given_config_source.scope = CONFIG_SCOPE_LOCAL;
+			opts->source.file = opts->file_to_free =
+				git_pathdup("config");
+		opts->source.scope = CONFIG_SCOPE_LOCAL;
 		free_worktrees(worktrees);
-	} else if (given_config_source.file) {
-		if (!is_absolute_path(given_config_source.file) && prefix)
-			given_config_source.file =
-				prefix_filename(prefix, given_config_source.file);
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
-	} else if (given_config_source.blob) {
-		given_config_source.scope = CONFIG_SCOPE_COMMAND;
+	} else if (opts->source.file) {
+		if (!is_absolute_path(opts->source.file) && prefix)
+			opts->source.file = opts->file_to_free =
+				prefix_filename(prefix, opts->source.file);
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
+	} else if (opts->source.blob) {
+		opts->source.scope = CONFIG_SCOPE_COMMAND;
 	}
 
-	if (respect_includes_opt == -1)
-		config_options.respect_includes = !given_config_source.file;
+	if (opts->respect_includes_opt == -1)
+		opts->options.respect_includes = !opts->source.file;
 	else
-		config_options.respect_includes = respect_includes_opt;
-	if (!nongit) {
-		config_options.commondir = get_git_common_dir();
-		config_options.git_dir = get_git_dir();
+		opts->options.respect_includes = opts->respect_includes_opt;
+	if (startup_info->have_repository) {
+		opts->options.commondir = get_git_common_dir();
+		opts->options.git_dir = get_git_dir();
+	}
+}
+
+static void location_options_release(struct config_location_options *opts)
+{
+	free(opts->file_to_free);
+}
+
+static void display_options_init(struct config_display_options *opts)
+{
+	if (opts->end_nul) {
+		opts->term = '\0';
+		opts->delim = '\n';
+		opts->key_delim = '\n';
+	}
+}
+
+static int cmd_config_list(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		CONFIG_DISPLAY_OPTIONS(display_opts),
+		OPT_GROUP(N_("Other")),
+		OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+			 N_("respect include directives on lookup")),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_list_usage, 0);
+	check_argc(argc, 0, 0);
+
+	location_options_init(&location_opts, prefix);
+	display_options_init(&display_opts);
+
+	setup_auto_pager("config", 1);
+
+	if (config_with_options(show_all_config, &display_opts,
+				&location_opts.source, the_repository,
+				&location_opts.options) < 0) {
+		if (location_opts.source.file)
+			die_errno(_("unable to read config file '%s'"),
+				  location_opts.source.file);
+		else
+			die(_("error processing config file(s)"));
 	}
 
-	if (end_nul) {
-		term = '\0';
-		delim = '\n';
-		key_delim = '\n';
+	location_options_release(&location_opts);
+	return 0;
+}
+
+static int cmd_config_get(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+	const char *value_pattern = NULL, *url = NULL;
+	int flags = 0;
+	unsigned get_value_flags = 0;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_GROUP(N_("Filter options")),
+		OPT_BIT(0, "all", &get_value_flags, N_("return all values for multi-valued config options"), GET_VALUE_ALL),
+		OPT_BIT(0, "regexp", &get_value_flags, N_("interpret the name as a regular expression"), GET_VALUE_KEY_REGEXP),
+		OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+		OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+		OPT_STRING(0, "url", &url, N_("URL"), N_("show config matching the given URL")),
+		CONFIG_DISPLAY_OPTIONS(display_opts),
+		OPT_GROUP(N_("Other")),
+		OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+			 N_("respect include directives on lookup")),
+		OPT_STRING(0, "default", &display_opts.default_value,
+			   N_("value"), N_("use default value when missing entry")),
+		OPT_END(),
+	};
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_get_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_argc(argc, 1, 1);
+
+	if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+		die(_("--fixed-value only applies with 'value-pattern'"));
+	if (display_opts.default_value &&
+	    ((get_value_flags & GET_VALUE_ALL) || url))
+		die(_("--default= cannot be used with --all or --url="));
+	if (url && ((get_value_flags & GET_VALUE_ALL) ||
+		    (get_value_flags & GET_VALUE_KEY_REGEXP) ||
+		    value_pattern))
+		die(_("--url= cannot be used with --all, --regexp or --value"));
+
+	location_options_init(&location_opts, prefix);
+	display_options_init(&display_opts);
+
+	setup_auto_pager("config", 1);
+
+	if (url)
+		ret = get_urlmatch(&location_opts, &display_opts, argv[0], url);
+	else
+		ret = get_value(&location_opts, &display_opts, argv[0], value_pattern,
+				get_value_flags, flags);
+
+	location_options_release(&location_opts);
+	return ret;
+}
+
+static int cmd_config_set(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	const char *value_pattern = NULL, *comment_arg = NULL;
+	char *comment = NULL;
+	int flags = 0, append = 0, type = 0;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		CONFIG_TYPE_OPTIONS(type),
+		OPT_GROUP(N_("Filter")),
+		OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+		OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+		OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+		OPT_GROUP(N_("Other")),
+		OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+		OPT_BOOL(0, "append", &append, N_("add a new line without altering any existing values")),
+		OPT_END(),
+	};
+	struct key_value_info default_kvi = KVI_INIT;
+	char *value;
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_set_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_argc(argc, 2, 2);
+
+	if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+		die(_("--fixed-value only applies with --value=<pattern>"));
+	if (append && value_pattern)
+		die(_("--append cannot be used with --value=<pattern>"));
+	if (append)
+		value_pattern = CONFIG_REGEX_NONE;
+
+	comment = git_config_prepare_comment_string(comment_arg);
+
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
+
+	value = normalize_value(argv[0], argv[1], type, &default_kvi);
+
+	if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) {
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+							     argv[0], value, value_pattern,
+							     comment, flags);
+	} else {
+		ret = git_config_set_in_file_gently(location_opts.source.file,
+						    argv[0], comment, value);
+		if (ret == CONFIG_NOTHING_SET)
+			error(_("cannot overwrite multiple values with a single value\n"
+			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
 	}
 
-	if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
+	location_options_release(&location_opts);
+	free(comment);
+	free(value);
+	return ret;
+}
+
+static int cmd_config_unset(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	const char *value_pattern = NULL;
+	int flags = 0;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_GROUP(N_("Filter")),
+		OPT_BIT(0, "all", &flags, N_("replace multi-valued config option with new value"), CONFIG_FLAGS_MULTI_REPLACE),
+		OPT_STRING(0, "value", &value_pattern, N_("pattern"), N_("show config with values matching the pattern")),
+		OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+		OPT_END(),
+	};
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_unset_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_argc(argc, 1, 1);
+
+	if ((flags & CONFIG_FLAGS_FIXED_VALUE) && !value_pattern)
+		die(_("--fixed-value only applies with 'value-pattern'"));
+
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
+
+	if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern)
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+							     argv[0], NULL, value_pattern,
+							     NULL, flags);
+	else
+		ret = git_config_set_in_file_gently(location_opts.source.file, argv[0],
+						    NULL, NULL);
+
+	location_options_release(&location_opts);
+	return ret;
+}
+
+static int cmd_config_rename_section(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_END(),
+	};
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_rename_section_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_argc(argc, 2, 2);
+
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
+
+	ret = git_config_rename_section_in_file(location_opts.source.file,
+						argv[0], argv[1]);
+	if (ret < 0)
+		goto out;
+	else if (!ret)
+		die(_("no such section: %s"), argv[0]);
+	ret = 0;
+
+out:
+	location_options_release(&location_opts);
+	return ret;
+}
+
+static int cmd_config_remove_section(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_END(),
+	};
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_remove_section_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+	check_argc(argc, 1, 1);
+
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
+
+	ret = git_config_rename_section_in_file(location_opts.source.file,
+						argv[0], NULL);
+	if (ret < 0)
+		goto out;
+	else if (!ret)
+		die(_("no such section: %s"), argv[0]);
+	ret = 0;
+
+out:
+	location_options_release(&location_opts);
+	return ret;
+}
+
+static int show_editor(struct config_location_options *opts)
+{
+	char *config_file;
+
+	if (!opts->source.file && !startup_info->have_repository)
+		die(_("not in a git directory"));
+	if (opts->source.use_stdin)
+		die(_("editing stdin is not supported"));
+	if (opts->source.blob)
+		die(_("editing blobs is not supported"));
+	git_config(git_default_config, NULL);
+	config_file = opts->source.file ?
+			xstrdup(opts->source.file) :
+			git_pathdup("config");
+	if (opts->use_global_config) {
+		int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
+		if (fd >= 0) {
+			char *content = default_user_config();
+			write_str_in_full(fd, content);
+			free(content);
+			close(fd);
+		}
+		else if (errno != EEXIST)
+			die_errno(_("cannot create configuration file %s"), config_file);
+	}
+	launch_editor(config_file, NULL, NULL);
+	free(config_file);
+
+	return 0;
+}
+
+static int cmd_config_edit(int argc, const char **argv, const char *prefix)
+{
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_END(),
+	};
+	int ret;
+
+	argc = parse_options(argc, argv, prefix, opts, builtin_config_edit_usage, 0);
+	check_argc(argc, 0, 0);
+
+	location_options_init(&location_opts, prefix);
+	check_write(&location_opts.source);
+
+	ret = show_editor(&location_opts);
+	location_options_release(&location_opts);
+	return ret;
+}
+
+static int cmd_config_actions(int argc, const char **argv, const char *prefix)
+{
+	enum {
+		ACTION_GET = (1<<0),
+		ACTION_GET_ALL = (1<<1),
+		ACTION_GET_REGEXP = (1<<2),
+		ACTION_REPLACE_ALL = (1<<3),
+		ACTION_ADD = (1<<4),
+		ACTION_UNSET = (1<<5),
+		ACTION_UNSET_ALL = (1<<6),
+		ACTION_RENAME_SECTION = (1<<7),
+		ACTION_REMOVE_SECTION = (1<<8),
+		ACTION_LIST = (1<<9),
+		ACTION_EDIT = (1<<10),
+		ACTION_SET = (1<<11),
+		ACTION_SET_ALL = (1<<12),
+		ACTION_GET_COLOR = (1<<13),
+		ACTION_GET_COLORBOOL = (1<<14),
+		ACTION_GET_URLMATCH = (1<<15),
+	};
+	struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT;
+	struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT;
+	const char *comment_arg = NULL;
+	int actions = 0;
+	unsigned flags = 0;
+	struct option opts[] = {
+		CONFIG_LOCATION_OPTIONS(location_opts),
+		OPT_GROUP(N_("Action")),
+		OPT_CMDMODE(0, "get", &actions, N_("get value: name [<value-pattern>]"), ACTION_GET),
+		OPT_CMDMODE(0, "get-all", &actions, N_("get all values: key [<value-pattern>]"), ACTION_GET_ALL),
+		OPT_CMDMODE(0, "get-regexp", &actions, N_("get values for regexp: name-regex [<value-pattern>]"), ACTION_GET_REGEXP),
+		OPT_CMDMODE(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
+		OPT_CMDMODE(0, "replace-all", &actions, N_("replace all matching variables: name value [<value-pattern>]"), ACTION_REPLACE_ALL),
+		OPT_CMDMODE(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+		OPT_CMDMODE(0, "unset", &actions, N_("remove a variable: name [<value-pattern>]"), ACTION_UNSET),
+		OPT_CMDMODE(0, "unset-all", &actions, N_("remove all matches: name [<value-pattern>]"), ACTION_UNSET_ALL),
+		OPT_CMDMODE(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+		OPT_CMDMODE(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+		OPT_CMDMODE('l', "list", &actions, N_("list all"), ACTION_LIST),
+		OPT_CMDMODE('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+		OPT_CMDMODE(0, "get-color", &actions, N_("find the color configured: slot [<default>]"), ACTION_GET_COLOR),
+		OPT_CMDMODE(0, "get-colorbool", &actions, N_("find the color setting: slot [<stdout-is-tty>]"), ACTION_GET_COLORBOOL),
+		CONFIG_DISPLAY_OPTIONS(display_opts),
+		OPT_GROUP(N_("Other")),
+		OPT_STRING(0, "default", &display_opts.default_value,
+			   N_("value"), N_("with --get, use default value when missing entry")),
+		OPT_STRING(0, "comment", &comment_arg, N_("value"), N_("human-readable comment string (# will be prepended as needed)")),
+		OPT_BIT(0, "fixed-value", &flags, N_("use string equality when comparing values to value pattern"), CONFIG_FLAGS_FIXED_VALUE),
+		OPT_BOOL(0, "includes", &location_opts.respect_includes_opt,
+			 N_("respect include directives on lookup")),
+		OPT_END(),
+	};
+	char *value = NULL, *comment = NULL;
+	int ret = 0;
+	struct key_value_info default_kvi = KVI_INIT;
+
+	argc = parse_options(argc, argv, prefix, opts,
+			     builtin_config_usage,
+			     PARSE_OPT_STOP_AT_NON_OPTION);
+
+	location_options_init(&location_opts, prefix);
+	display_options_init(&display_opts);
+
+	if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && display_opts.type) {
 		error(_("--get-color and variable type are incoherent"));
-		usage_builtin_config();
+		exit(129);
 	}
 
-	if (HAS_MULTI_BITS(actions)) {
-		error(_("only one action at a time"));
-		usage_builtin_config();
-	}
 	if (actions == 0)
 		switch (argc) {
 		case 1: actions = ACTION_GET; break;
 		case 2: actions = ACTION_SET; break;
 		case 3: actions = ACTION_SET_ALL; break;
 		default:
-			usage_builtin_config();
+			error(_("no action specified"));
+			exit(129);
 		}
-	if (omit_values &&
+	if (display_opts.omit_values &&
 	    !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
 		error(_("--name-only is only applicable to --list or --get-regexp"));
-		usage_builtin_config();
+		exit(129);
 	}
 
-	if (show_origin && !(actions &
+	if (display_opts.show_origin && !(actions &
 		(ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
 		error(_("--show-origin is only applicable to --get, --get-all, "
 			"--get-regexp, and --list"));
-		usage_builtin_config();
+		exit(129);
 	}
 
-	if (default_value && !(actions & ACTION_GET)) {
+	if (display_opts.default_value && !(actions & ACTION_GET)) {
 		error(_("--default is only applicable to --get"));
-		usage_builtin_config();
+		exit(129);
 	}
 
-	if (comment &&
+	if (comment_arg &&
 	    !(actions & (ACTION_ADD|ACTION_SET|ACTION_SET_ALL|ACTION_REPLACE_ALL))) {
 		error(_("--comment is only applicable to add/set/replace operations"));
-		usage_builtin_config();
+		exit(129);
 	}
 
 	/* check usage of --fixed-value */
-	if (fixed_value) {
+	if (flags & CONFIG_FLAGS_FIXED_VALUE) {
 		int allowed_usage = 0;
 
 		switch (actions) {
@@ -835,148 +1250,125 @@
 
 		if (!allowed_usage) {
 			error(_("--fixed-value only applies with 'value-pattern'"));
-			usage_builtin_config();
+			exit(129);
 		}
-
-		flags |= CONFIG_FLAGS_FIXED_VALUE;
 	}
 
-	comment = git_config_prepare_comment_string(comment);
+	comment = git_config_prepare_comment_string(comment_arg);
 
-	if (actions & PAGING_ACTIONS)
+	/*
+	 * The following actions may produce more than one line of output and
+	 * should therefore be paged.
+	 */
+	if (actions & (ACTION_LIST | ACTION_GET_ALL | ACTION_GET_REGEXP | ACTION_GET_URLMATCH))
 		setup_auto_pager("config", 1);
 
 	if (actions == ACTION_LIST) {
 		check_argc(argc, 0, 0);
-		if (config_with_options(show_all_config, NULL,
-					&given_config_source, the_repository,
-					&config_options) < 0) {
-			if (given_config_source.file)
+		if (config_with_options(show_all_config, &display_opts,
+					&location_opts.source, the_repository,
+					&location_opts.options) < 0) {
+			if (location_opts.source.file)
 				die_errno(_("unable to read config file '%s'"),
-					  given_config_source.file);
+					  location_opts.source.file);
 			else
 				die(_("error processing config file(s)"));
 		}
 	}
 	else if (actions == ACTION_EDIT) {
-		char *config_file;
-
-		check_argc(argc, 0, 0);
-		if (!given_config_source.file && nongit)
-			die(_("not in a git directory"));
-		if (given_config_source.use_stdin)
-			die(_("editing stdin is not supported"));
-		if (given_config_source.blob)
-			die(_("editing blobs is not supported"));
-		git_config(git_default_config, NULL);
-		config_file = given_config_source.file ?
-				xstrdup(given_config_source.file) :
-				git_pathdup("config");
-		if (use_global_config) {
-			int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
-			if (fd >= 0) {
-				char *content = default_user_config();
-				write_str_in_full(fd, content);
-				free(content);
-				close(fd);
-			}
-			else if (errno != EEXIST)
-				die_errno(_("cannot create configuration file %s"), config_file);
-		}
-		launch_editor(config_file, NULL, NULL);
-		free(config_file);
+		ret = show_editor(&location_opts);
 	}
 	else if (actions == ACTION_SET) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_in_file_gently(given_config_source.file, argv[0], comment, value);
+		value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+		ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value);
 		if (ret == CONFIG_NOTHING_SET)
 			error(_("cannot overwrite multiple values with a single value\n"
 			"       Use a regexp, --add or --replace-all to change %s."), argv[0]);
 	}
 	else if (actions == ACTION_SET_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value, argv[2],
 							     comment, flags);
 	}
 	else if (actions == ACTION_ADD) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
-		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value,
 							     CONFIG_REGEX_NONE,
 							     comment, flags);
 	}
 	else if (actions == ACTION_REPLACE_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 3);
-		value = normalize_value(argv[0], argv[1], &default_kvi);
-		ret = git_config_set_multivar_in_file_gently(given_config_source.file,
+		value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi);
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
 							     argv[0], value, argv[2],
 							     comment, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_GET) {
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+				0, flags);
 	}
 	else if (actions == ACTION_GET_ALL) {
-		do_all = 1;
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+				GET_VALUE_ALL, flags);
 	}
 	else if (actions == ACTION_GET_REGEXP) {
-		show_keys = 1;
-		use_key_regexp = 1;
-		do_all = 1;
+		display_opts.show_keys = 1;
 		check_argc(argc, 1, 2);
-		return get_value(argv[0], argv[1], flags);
+		ret = get_value(&location_opts, &display_opts, argv[0], argv[1],
+				GET_VALUE_ALL|GET_VALUE_KEY_REGEXP, flags);
 	}
 	else if (actions == ACTION_GET_URLMATCH) {
 		check_argc(argc, 2, 2);
-		return get_urlmatch(argv[0], argv[1]);
+		ret = get_urlmatch(&location_opts, &display_opts, argv[0], argv[1]);
 	}
 	else if (actions == ACTION_UNSET) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 2);
 		if (argc == 2)
-			return git_config_set_multivar_in_file_gently(given_config_source.file,
-								      argv[0], NULL, argv[1],
-								      NULL, flags);
+			ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+								     argv[0], NULL, argv[1],
+								     NULL, flags);
 		else
-			return git_config_set_in_file_gently(given_config_source.file,
-							     argv[0], NULL, NULL);
+			ret = git_config_set_in_file_gently(location_opts.source.file,
+							    argv[0], NULL, NULL);
 	}
 	else if (actions == ACTION_UNSET_ALL) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 2);
-		return git_config_set_multivar_in_file_gently(given_config_source.file,
-							      argv[0], NULL, argv[1],
-							      NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
+		ret = git_config_set_multivar_in_file_gently(location_opts.source.file,
+							     argv[0], NULL, argv[1],
+							     NULL, flags | CONFIG_FLAGS_MULTI_REPLACE);
 	}
 	else if (actions == ACTION_RENAME_SECTION) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 2, 2);
-		ret = git_config_rename_section_in_file(given_config_source.file,
+		ret = git_config_rename_section_in_file(location_opts.source.file,
 							argv[0], argv[1]);
 		if (ret < 0)
-			return ret;
+			goto out;
 		else if (!ret)
 			die(_("no such section: %s"), argv[0]);
 		else
 			ret = 0;
 	}
 	else if (actions == ACTION_REMOVE_SECTION) {
-		check_write();
+		check_write(&location_opts.source);
 		check_argc(argc, 1, 1);
-		ret = git_config_rename_section_in_file(given_config_source.file,
+		ret = git_config_rename_section_in_file(location_opts.source.file,
 							argv[0], NULL);
 		if (ret < 0)
-			return ret;
+			goto out;
 		else if (!ret)
 			die(_("no such section: %s"), argv[0]);
 		else
@@ -984,15 +1376,51 @@
 	}
 	else if (actions == ACTION_GET_COLOR) {
 		check_argc(argc, 1, 2);
-		get_color(argv[0], argv[1]);
+		get_color(&location_opts, argv[0], argv[1]);
 	}
 	else if (actions == ACTION_GET_COLORBOOL) {
 		check_argc(argc, 1, 2);
 		if (argc == 2)
 			color_stdout_is_tty = git_config_bool("command line", argv[1]);
-		return get_colorbool(argv[0], argc == 2);
+		ret = get_colorbool(&location_opts, argv[0], argc == 2);
 	}
 
+out:
+	location_options_release(&location_opts);
+	free(comment);
 	free(value);
 	return ret;
 }
+
+int cmd_config(int argc, const char **argv, const char *prefix)
+{
+	parse_opt_subcommand_fn *subcommand = NULL;
+	struct option subcommand_opts[] = {
+		OPT_SUBCOMMAND("list", &subcommand, cmd_config_list),
+		OPT_SUBCOMMAND("get", &subcommand, cmd_config_get),
+		OPT_SUBCOMMAND("set", &subcommand, cmd_config_set),
+		OPT_SUBCOMMAND("unset", &subcommand, cmd_config_unset),
+		OPT_SUBCOMMAND("rename-section", &subcommand, cmd_config_rename_section),
+		OPT_SUBCOMMAND("remove-section", &subcommand, cmd_config_remove_section),
+		OPT_SUBCOMMAND("edit", &subcommand, cmd_config_edit),
+		OPT_END(),
+	};
+
+	/*
+	 * This is somewhat hacky: we first parse the command line while
+	 * keeping all args intact in order to determine whether a subcommand
+	 * has been specified. If so, we re-parse it a second time, but this
+	 * time we drop KEEP_ARGV0. This is so that we don't munge the command
+	 * line in case no subcommand was given, which would otherwise confuse
+	 * us when parsing the legacy-style modes that don't use subcommands.
+	 */
+	argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+			     PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_ARGV0|PARSE_OPT_KEEP_UNKNOWN_OPT);
+	if (subcommand) {
+		argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage,
+		       PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT);
+		return subcommand(argc, argv, prefix);
+	}
+
+	return cmd_config_actions(argc, argv, prefix);
+}
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index 17f929d..4952b22 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -115,7 +115,9 @@
 		return error("client sent bogus timeout line: %s", item.buf);
 	*timeout = atoi(p);
 
-	if (credential_read(c, fh) < 0)
+	credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
+
+	if (credential_read(c, fh, CREDENTIAL_OP_HELPER) < 0)
 		return -1;
 	return 0;
 }
@@ -131,8 +133,18 @@
 	else if (!strcmp(action.buf, "get")) {
 		struct credential_cache_entry *e = lookup_credential(&c);
 		if (e) {
-			fprintf(out, "username=%s\n", e->item.username);
-			fprintf(out, "password=%s\n", e->item.password);
+			e->item.capa_authtype.request_initial = 1;
+			e->item.capa_authtype.request_helper = 1;
+
+			fprintf(out, "capability[]=authtype\n");
+			if (e->item.username)
+				fprintf(out, "username=%s\n", e->item.username);
+			if (e->item.password)
+				fprintf(out, "password=%s\n", e->item.password);
+			if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.authtype)
+				fprintf(out, "authtype=%s\n", e->item.authtype);
+			if (credential_has_capability(&c.capa_authtype, CREDENTIAL_OP_HELPER) && e->item.credential)
+				fprintf(out, "credential=%s\n", e->item.credential);
 			if (e->item.password_expiry_utc != TIME_MAX)
 				fprintf(out, "password_expiry_utc=%"PRItime"\n",
 					e->item.password_expiry_utc);
@@ -157,8 +169,10 @@
 	else if (!strcmp(action.buf, "store")) {
 		if (timeout < 0)
 			warning("cache client didn't specify a timeout");
-		else if (!c.username || !c.password)
+		else if ((!c.username || !c.password) && (!c.authtype && !c.credential))
 			warning("cache client gave us a partial credential");
+		else if (c.ephemeral)
+			warning("not storing ephemeral credential");
 		else {
 			remove_credential(&c, 0);
 			cache_credential(&c, timeout);
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index bef120b..3db8df7 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "credential.h"
 #include "gettext.h"
 #include "parse-options.h"
 #include "path.h"
@@ -127,6 +128,13 @@
 	return socket;
 }
 
+static void announce_capabilities(void)
+{
+	struct credential c = CREDENTIAL_INIT;
+	c.capa_authtype.request_initial = 1;
+	credential_announce_capabilities(&c, stdout);
+}
+
 int cmd_credential_cache(int argc, const char **argv, const char *prefix)
 {
 	char *socket_path = NULL;
@@ -163,6 +171,8 @@
 		do_cache(socket_path, op, timeout, FLAG_RELAY);
 	else if (!strcmp(op, "store"))
 		do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN);
+	else if (!strcmp(op, "capability"))
+		announce_capabilities();
 	else
 		; /* ignore unknown operation */
 
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 4a49241..494c809 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -205,7 +205,7 @@
 	if (!fns.nr)
 		die("unable to set up default path; use --file");
 
-	if (credential_read(&c, stdin) < 0)
+	if (credential_read(&c, stdin, CREDENTIAL_OP_HELPER) < 0)
 		die("unable to read credential");
 
 	if (!strcmp(op, "get"))
diff --git a/builtin/credential.c b/builtin/credential.c
index 7010752..b72e76d 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -17,18 +17,29 @@
 		usage(usage_msg);
 	op = argv[1];
 
-	if (credential_read(&c, stdin) < 0)
+	if (!strcmp(op, "capability")) {
+		credential_set_all_capabilities(&c, CREDENTIAL_OP_INITIAL);
+		credential_announce_capabilities(&c, stdout);
+		return 0;
+	}
+
+	if (credential_read(&c, stdin, CREDENTIAL_OP_INITIAL) < 0)
 		die("unable to read credential from stdin");
 
 	if (!strcmp(op, "fill")) {
-		credential_fill(&c);
-		credential_write(&c, stdout);
+		credential_fill(&c, 0);
+		credential_next_state(&c);
+		credential_write(&c, stdout, CREDENTIAL_OP_RESPONSE);
 	} else if (!strcmp(op, "approve")) {
+		credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
 		credential_approve(&c);
 	} else if (!strcmp(op, "reject")) {
+		credential_set_all_capabilities(&c, CREDENTIAL_OP_HELPER);
 		credential_reject(&c);
 	} else {
 		usage(usage_msg);
 	}
+
+	credential_clear(&c);
 	return 0;
 }
diff --git a/builtin/describe.c b/builtin/describe.c
index d6c77a7..e5287ed 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -201,7 +200,7 @@
 	}
 
 	/* Is it annotated? */
-	if (!peel_iterated_oid(oid, &peeled)) {
+	if (!peel_iterated_oid(the_repository, oid, &peeled)) {
 		is_annotated = !oideq(oid, &peeled);
 	} else {
 		oidcpy(&peeled, oid);
@@ -638,7 +637,8 @@
 	}
 
 	hashmap_init(&names, commit_name_neq, NULL, 0);
-	for_each_rawref(get_name, NULL);
+	refs_for_each_rawref(get_main_ref_store(the_repository), get_name,
+			     NULL);
 	if (!hashmap_get_size(&names) && !always)
 		die(_("No names found, cannot describe anything."));
 
@@ -674,7 +674,7 @@
 			prepare_repo_settings(the_repository);
 			the_repository->settings.command_requires_full_index = 0;
 			repo_read_index(the_repository);
-			refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
+			refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED,
 				      NULL, NULL, NULL);
 			fd = repo_hold_locked_index(the_repository,
 						    &index_lock, 0);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index a8e68ce..0d3c611 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "diff.h"
@@ -206,7 +205,7 @@
 		opt->diffopt.rotate_to_strict = 0;
 		opt->diffopt.no_free = 1;
 		if (opt->diffopt.detect_rename) {
-			if (!the_index.cache)
+			if (the_repository->index->cache)
 				repo_read_index(the_repository);
 			opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
 		}
diff --git a/builtin/diff.c b/builtin/diff.c
index 6e196e0..9b6cdab 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "ewah/ewok.h"
@@ -239,9 +239,9 @@
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
 	if (fd < 0)
 		return;
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	repo_read_index(the_repository);
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL,
 		      NULL);
 	repo_update_index_if_able(the_repository, &lock_file);
 }
@@ -465,6 +465,15 @@
 			no_index = DIFF_NO_INDEX_IMPLICIT;
 	}
 
+	/*
+	 * When operating outside of a Git repository we need to have a hash
+	 * algorithm at hand so that we can generate the blob hashes. We
+	 * default to SHA1 here, but may eventually want to change this to be
+	 * configurable via a command line option.
+	 */
+	if (nongit)
+		repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
 	init_diff_ui_defaults();
 	git_config(git_diff_ui_config, NULL);
 	prefix = precompose_argv_prefix(argc, argv, prefix);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index a3c72b8..a1794b7 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -11,7 +11,7 @@
  *
  * Copyright (C) 2016 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -117,7 +117,7 @@
 		int fd = open(buf.buf, O_RDONLY);
 
 		if (fd >= 0 &&
-		    !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+		    !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
 			if (is_null_oid(oid)) {
 				oidcpy(oid, &wt_oid);
 				use = 1;
@@ -674,19 +674,15 @@
 static int run_file_diff(int prompt, const char *prefix,
 			 struct child_process *child)
 {
-	const char *env[] = {
-		"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
-		NULL
-	};
-
+	strvec_push(&child->env, "GIT_PAGER=");
+	strvec_push(&child->env, "GIT_EXTERNAL_DIFF=git-difftool--helper");
 	if (prompt > 0)
-		env[2] = "GIT_DIFFTOOL_PROMPT=true";
+		strvec_push(&child->env, "GIT_DIFFTOOL_PROMPT=true");
 	else if (!prompt)
-		env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+		strvec_push(&child->env, "GIT_DIFFTOOL_NO_PROMPT=true");
 
 	child->git_cmd = 1;
 	child->dir = prefix;
-	strvec_pushv(&child->env, env);
 
 	return run_command(child);
 }
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index dc5a9d3..d1c0243 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1604,10 +1604,11 @@
 
 	if (is_null_oid(&b->oid)) {
 		if (b->delete)
-			delete_ref(NULL, b->name, NULL, 0);
+			refs_delete_ref(get_main_ref_store(the_repository),
+					NULL, b->name, NULL, 0);
 		return 0;
 	}
-	if (read_ref(b->name, &old_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), b->name, &old_oid))
 		oidclr(&old_oid);
 	if (!force_update && !is_null_oid(&old_oid)) {
 		struct commit *old_cmit, *new_cmit;
@@ -1631,10 +1632,11 @@
 			return -1;
 		}
 	}
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
-				   0, msg, &err) ||
+				   NULL, NULL, 0, msg, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
 		ref_transaction_free(transaction);
 		error("%s", err.buf);
@@ -1665,7 +1667,8 @@
 	struct strbuf err = STRBUF_INIT;
 	struct ref_transaction *transaction;
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction) {
 		failure |= error("%s", err.buf);
 		goto cleanup;
@@ -1675,7 +1678,8 @@
 		strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 
 		if (ref_transaction_update(transaction, ref_name.buf,
-					   &t->oid, NULL, 0, msg, &err)) {
+					   &t->oid, NULL, NULL, NULL,
+					   0, msg, &err)) {
 			failure |= error("%s", err.buf);
 			goto cleanup;
 		}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5857d86..a319954 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -340,7 +340,8 @@
 	refname_hash_init(&remote_refs);
 	create_fetch_oidset(head, &fetch_oids);
 
-	for_each_ref(add_one_refname, &existing_refs);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_one_refname,
+			  &existing_refs);
 
 	/*
 	 * If we already have a transaction, then we need to filter out all
@@ -614,7 +615,9 @@
 
 			if (!existing_refs_populated) {
 				refname_hash_init(&existing_refs);
-				for_each_ref(add_one_refname, &existing_refs);
+				refs_for_each_ref(get_main_ref_store(the_repository),
+						  add_one_refname,
+						  &existing_refs);
 				existing_refs_populated = 1;
 			}
 
@@ -659,7 +662,8 @@
 	 * lifecycle.
 	 */
 	if (!transaction) {
-		transaction = our_transaction = ref_transaction_begin(&err);
+		transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+									    &err);
 		if (!transaction) {
 			ret = STORE_REF_ERROR_OTHER;
 			goto out;
@@ -668,7 +672,7 @@
 
 	ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
 				     check_old ? &ref->old_oid : NULL,
-				     0, msg, &err);
+				     NULL, NULL, 0, msg, &err);
 	if (ret) {
 		ret = STORE_REF_ERROR_OTHER;
 		goto out;
@@ -1393,7 +1397,9 @@
 			for (ref = stale_refs; ref; ref = ref->next)
 				string_list_append(&refnames, ref->name);
 
-			result = delete_refs("fetch: prune", &refnames, 0);
+			result = refs_delete_refs(get_main_ref_store(the_repository),
+						  "fetch: prune", &refnames,
+						  0);
 			string_list_clear(&refnames, 0);
 		}
 	}
@@ -1406,7 +1412,8 @@
 					   _("(none)"), ref->name,
 					   &ref->new_oid, &ref->old_oid,
 					   summary_width);
-			warn_dangling_symref(stderr, dangling_msg, ref->name);
+			refs_warn_dangling_symref(get_main_ref_store(the_repository),
+						  stderr, dangling_msg, ref->name);
 		}
 	}
 
@@ -1479,7 +1486,8 @@
 			continue;
 		}
 		old_nr = oids->nr;
-		for_each_glob_ref(add_oid, s, oids);
+		refs_for_each_glob_ref(get_main_ref_store(the_repository),
+				       add_oid, s, oids);
 		if (old_nr == oids->nr)
 			warning("ignoring --negotiation-tip=%s because it does not match any refs",
 				s);
@@ -1655,7 +1663,8 @@
 			   config->display_format);
 
 	if (atomic_fetch) {
-		transaction = ref_transaction_begin(&err);
+		transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+							  &err);
 		if (!transaction) {
 			retcode = -1;
 			goto cleanup;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 919282e..5517a4a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -98,7 +98,7 @@
 	}
 
 	if (include_root_refs)
-		flags |= FILTER_REFS_ROOT_REFS;
+		flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD;
 
 	filter.match_as_path = 1;
 	filter_and_format_refs(&filter, flags, sorting, &format);
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 28186b3..c4fa41f 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -32,6 +32,7 @@
 int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
 {
 	static const char *config_key = NULL;
+	int keep_going = 0;
 	int i, result = 0;
 	const struct string_list *values;
 	int err;
@@ -39,6 +40,8 @@
 	const struct option options[] = {
 		OPT_STRING(0, "config", &config_key, N_("config"),
 			   N_("config key storing a list of repository paths")),
+		OPT_BOOL(0, "keep-going", &keep_going,
+			 N_("keep going even if command fails in a repository")),
 		OPT_END()
 	};
 
@@ -55,8 +58,14 @@
 	else if (err)
 		return 0;
 
-	for (i = 0; !result && i < values->nr; i++)
-		result = run_command_on_repo(values->items[i].string, argc, argv);
+	for (i = 0; i < values->nr; i++) {
+		int ret = run_command_on_repo(values->items[i].string, argc, argv);
+		if (ret) {
+			if (!keep_going)
+					return ret;
+			result = 1;
+		}
+	}
 
 	return result;
 }
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f892487..d13a226 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -514,7 +514,9 @@
 	struct strbuf refname = STRBUF_INIT;
 
 	strbuf_worktree_ref(cb_data, &refname, logname);
-	for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+				 refname.buf, fsck_handle_reflog_ent,
+				 refname.buf);
 	strbuf_release(&refname);
 	return 0;
 }
@@ -563,7 +565,8 @@
 	const char *head_points_at;
 	struct object_id head_oid;
 
-	for_each_rawref(fsck_handle_ref, NULL);
+	refs_for_each_rawref(get_main_ref_store(the_repository),
+			     fsck_handle_ref, NULL);
 
 	worktrees = get_worktrees();
 	for (p = worktrees; *p; p++) {
@@ -712,7 +715,9 @@
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s link"), head_ref_name);
 
-	*head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+	*head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						  head_ref_name, 0, head_oid,
+						  NULL);
 	if (!*head_points_at) {
 		errors_found |= ERROR_REFS;
 		return error(_("invalid %s"), head_ref_name);
diff --git a/builtin/gc.c b/builtin/gc.c
index d187cec..72bac25 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -846,7 +846,7 @@
 	struct commit_list *stack = NULL;
 	struct commit *commit;
 
-	if (!peel_iterated_oid(oid, &peeled))
+	if (!peel_iterated_oid(the_repository, oid, &peeled))
 		oid = &peeled;
 	if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
 		return 0;
@@ -907,7 +907,8 @@
 	if (data.limit < 0)
 		return 1;
 
-	result = for_each_ref(dfs_on_ref, &data);
+	result = refs_for_each_ref(get_main_ref_store(the_repository),
+				   dfs_on_ref, &data);
 
 	repo_clear_commit_marks(the_repository, SEEN);
 
@@ -1870,6 +1871,7 @@
 		   "<string>%s/git</string>\n"
 		   "<string>--exec-path=%s</string>\n"
 		   "<string>for-each-repo</string>\n"
+		   "<string>--keep-going</string>\n"
 		   "<string>--config=maintenance.repo</string>\n"
 		   "<string>maintenance</string>\n"
 		   "<string>run</string>\n"
@@ -2112,7 +2114,7 @@
 	      "<Actions Context=\"Author\">\n"
 	      "<Exec>\n"
 	      "<Command>\"%s\\headless-git.exe\"</Command>\n"
-	      "<Arguments>--exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
+	      "<Arguments>--exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n"
 	      "</Exec>\n"
 	      "</Actions>\n"
 	      "</Task>\n";
@@ -2257,7 +2259,7 @@
 			"# replaced in the future by a Git command.\n\n");
 
 		strbuf_addf(&line_format,
-			    "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n",
+			    "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n",
 			    exec_path, exec_path);
 		fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly");
 		fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily");
@@ -2458,7 +2460,7 @@
 	       "\n"
 	       "[Service]\n"
 	       "Type=oneshot\n"
-	       "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n"
+	       "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n"
 	       "LockPersonality=yes\n"
 	       "MemoryDenyWriteExecute=yes\n"
 	       "NoNewPrivileges=yes\n"
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 82ca6d2..c767414 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -123,6 +123,9 @@
 	else
 		prefix = setup_git_directory_gently(&nongit);
 
+	if (nongit && !the_hash_algo)
+		repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
 	if (vpath && prefix) {
 		vpath_free = prefix_filename(prefix, vpath);
 		vpath = vpath_free;
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 0170469..582dcf2 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -81,7 +81,7 @@
 	const char *ref_format = NULL;
 	const char *initial_branch = NULL;
 	int hash_algo = GIT_HASH_UNKNOWN;
-	unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
+	enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
 	int init_shared_repository = -1;
 	const struct option init_db_options[] = {
 		OPT_STRING(0, "template", &template_dir, N_("template-directory"),
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 8768bfe..1d96949 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -141,7 +141,7 @@
 	LIST_HEAD(head);
 	struct strbuf sb = STRBUF_INIT;
 	struct strbuf trailer_block = STRBUF_INIT;
-	struct trailer_info info;
+	struct trailer_info *info;
 	FILE *outfile = stdout;
 
 	trailer_config_init();
@@ -151,13 +151,13 @@
 	if (opts->in_place)
 		outfile = create_in_place_tempfile(file);
 
-	parse_trailers(opts, &info, sb.buf, &head);
+	info = parse_trailers(opts, sb.buf, &head);
 
 	/* Print the lines before the trailers */
 	if (!opts->only_trailers)
-		fwrite(sb.buf, 1, info.trailer_block_start, outfile);
+		fwrite(sb.buf, 1, trailer_block_start(info), outfile);
 
-	if (!opts->only_trailers && !info.blank_line_before_trailer)
+	if (!opts->only_trailers && !blank_line_before_trailer_block(info))
 		fprintf(outfile, "\n");
 
 
@@ -178,8 +178,8 @@
 
 	/* Print the lines after the trailers as is */
 	if (!opts->only_trailers)
-		fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
-	trailer_info_release(&info);
+		fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile);
+	trailer_info_release(info);
 
 	if (opts->in_place)
 		if (rename_tempfile(&trailers_tempfile, file))
diff --git a/builtin/log.c b/builtin/log.c
index c0a8bb9..78a247d 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -48,22 +48,8 @@
 #define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
 #define FORMAT_PATCH_NAME_MAX_DEFAULT 64
 
-/* Set a default date-time format for git log ("log.date" config variable) */
-static const char *default_date_mode = NULL;
-
-static int default_abbrev_commit;
-static int default_show_root = 1;
-static int default_follow;
-static int default_show_signature;
-static int default_encode_email_headers = 1;
-static int decoration_style;
-static int decoration_given;
-static int use_mailmap_config = 1;
 static unsigned int force_in_body_from;
 static int stdout_mboxrd;
-static const char *fmt_patch_subject_prefix = "PATCH";
-static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
-static const char *fmt_pretty;
 static int format_no_prefix;
 
 static const char * const builtin_log_usage[] = {
@@ -111,6 +97,39 @@
 	return -1;
 }
 
+struct log_config {
+	int default_abbrev_commit;
+	int default_show_root;
+	int default_follow;
+	int default_show_signature;
+	int default_encode_email_headers;
+	int decoration_style;
+	int decoration_given;
+	int use_mailmap_config;
+	char *fmt_patch_subject_prefix;
+	int fmt_patch_name_max;
+	char *fmt_pretty;
+	char *default_date_mode;
+};
+
+static void log_config_init(struct log_config *cfg)
+{
+	memset(cfg, 0, sizeof(*cfg));
+	cfg->default_show_root = 1;
+	cfg->default_encode_email_headers = 1;
+	cfg->use_mailmap_config = 1;
+	cfg->fmt_patch_subject_prefix = xstrdup("PATCH");
+	cfg->fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
+	cfg->decoration_style = auto_decoration_style();
+}
+
+static void log_config_release(struct log_config *cfg)
+{
+	free(cfg->default_date_mode);
+	free(cfg->fmt_pretty);
+	free(cfg->fmt_patch_subject_prefix);
+}
+
 static int use_default_decoration_filter = 1;
 static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
 static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
@@ -127,20 +146,22 @@
 	return 0;
 }
 
-static int decorate_callback(const struct option *opt UNUSED, const char *arg,
+static int decorate_callback(const struct option *opt, const char *arg,
 			     int unset)
 {
-	if (unset)
-		decoration_style = 0;
-	else if (arg)
-		decoration_style = parse_decoration_style(arg);
-	else
-		decoration_style = DECORATE_SHORT_REFS;
+	struct log_config *cfg = opt->value;
 
-	if (decoration_style < 0)
+	if (unset)
+		cfg->decoration_style = 0;
+	else if (arg)
+		cfg->decoration_style = parse_decoration_style(arg);
+	else
+		cfg->decoration_style = DECORATE_SHORT_REFS;
+
+	if (cfg->decoration_style < 0)
 		die(_("invalid --decorate option: %s"), arg);
 
-	decoration_given = 1;
+	cfg->decoration_given = 1;
 
 	return 0;
 }
@@ -160,32 +181,26 @@
 	return 0;
 }
 
-static void init_log_defaults(void)
+static void cmd_log_init_defaults(struct rev_info *rev,
+				  struct log_config *cfg)
 {
-	init_diff_ui_defaults();
-
-	decoration_style = auto_decoration_style();
-}
-
-static void cmd_log_init_defaults(struct rev_info *rev)
-{
-	if (fmt_pretty)
-		get_commit_format(fmt_pretty, rev);
-	if (default_follow)
+	if (cfg->fmt_pretty)
+		get_commit_format(cfg->fmt_pretty, rev);
+	if (cfg->default_follow)
 		rev->diffopt.flags.default_follow_renames = 1;
 	rev->verbose_header = 1;
 	init_diffstat_widths(&rev->diffopt);
 	rev->diffopt.flags.recursive = 1;
 	rev->diffopt.flags.allow_textconv = 1;
-	rev->abbrev_commit = default_abbrev_commit;
-	rev->show_root_diff = default_show_root;
-	rev->subject_prefix = fmt_patch_subject_prefix;
-	rev->patch_name_max = fmt_patch_name_max;
-	rev->show_signature = default_show_signature;
-	rev->encode_email_headers = default_encode_email_headers;
+	rev->abbrev_commit = cfg->default_abbrev_commit;
+	rev->show_root_diff = cfg->default_show_root;
+	rev->subject_prefix = cfg->fmt_patch_subject_prefix;
+	rev->patch_name_max = cfg->fmt_patch_name_max;
+	rev->show_signature = cfg->default_show_signature;
+	rev->encode_email_headers = cfg->default_encode_email_headers;
 
-	if (default_date_mode)
-		parse_date_format(default_date_mode, &rev->date_mode);
+	if (cfg->default_date_mode)
+		parse_date_format(cfg->default_date_mode, &rev->date_mode);
 }
 
 static void set_default_decoration_filter(struct decoration_filter *decoration_filter)
@@ -233,7 +248,8 @@
 }
 
 static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
-			 struct rev_info *rev, struct setup_revision_opt *opt)
+			 struct rev_info *rev, struct setup_revision_opt *opt,
+			 struct log_config *cfg)
 {
 	struct userformat_want w;
 	int quiet = 0, source = 0, mailmap;
@@ -258,7 +274,7 @@
 				N_("pattern"), N_("only decorate refs that match <pattern>")),
 		OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
 				N_("pattern"), N_("do not decorate refs that match <pattern>")),
-		OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
+		OPT_CALLBACK_F(0, "decorate", cfg, NULL, N_("decorate options"),
 			       PARSE_OPT_OPTARG, decorate_callback),
 		OPT_CALLBACK('L', NULL, &line_cb, "range:file",
 			     N_("trace the evolution of line range <start>,<end> or function :<funcname> in <file>"),
@@ -269,7 +285,7 @@
 	line_cb.rev = rev;
 	line_cb.prefix = prefix;
 
-	mailmap = use_mailmap_config;
+	mailmap = cfg->use_mailmap_config;
 	argc = parse_options(argc, argv, prefix,
 			     builtin_log_options, builtin_log_usage,
 			     PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -314,8 +330,8 @@
 		 * "log --pretty=raw" is special; ignore UI oriented
 		 * configuration variables such as decoration.
 		 */
-		if (!decoration_given)
-			decoration_style = 0;
+		if (!cfg->decoration_given)
+			cfg->decoration_style = 0;
 		if (!rev->abbrev_commit_given)
 			rev->abbrev_commit = 0;
 	}
@@ -326,24 +342,24 @@
 			 * Disable decoration loading if the format will not
 			 * show them anyway.
 			 */
-			decoration_style = 0;
-		} else if (!decoration_style) {
+			cfg->decoration_style = 0;
+		} else if (!cfg->decoration_style) {
 			/*
 			 * If we are going to show them, make sure we do load
 			 * them here, but taking care not to override a
 			 * specific style set by config or --decorate.
 			 */
-			decoration_style = DECORATE_SHORT_REFS;
+			cfg->decoration_style = DECORATE_SHORT_REFS;
 		}
 	}
 
-	if (decoration_style || rev->simplify_by_decoration) {
+	if (cfg->decoration_style || rev->simplify_by_decoration) {
 		set_default_decoration_filter(&decoration_filter);
 
-		if (decoration_style)
+		if (cfg->decoration_style)
 			rev->show_decorations = 1;
 
-		load_ref_decorations(&decoration_filter, decoration_style);
+		load_ref_decorations(&decoration_filter, cfg->decoration_style);
 	}
 
 	if (rev->line_level_traverse)
@@ -353,16 +369,11 @@
 }
 
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
-			 struct rev_info *rev, struct setup_revision_opt *opt)
+			 struct rev_info *rev, struct setup_revision_opt *opt,
+			 struct log_config *cfg)
 {
-	cmd_log_init_defaults(rev);
-	cmd_log_init_finish(argc, argv, prefix, rev, opt);
-}
-
-static int cmd_log_deinit(int ret, struct rev_info *rev)
-{
-	release_revisions(rev);
-	return ret;
+	cmd_log_init_defaults(rev, cfg);
+	cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg);
 }
 
 /*
@@ -566,30 +577,37 @@
 static int git_log_config(const char *var, const char *value,
 			  const struct config_context *ctx, void *cb)
 {
+	struct log_config *cfg = cb;
 	const char *slot_name;
 
-	if (!strcmp(var, "format.pretty"))
-		return git_config_string(&fmt_pretty, var, value);
-	if (!strcmp(var, "format.subjectprefix"))
-		return git_config_string(&fmt_patch_subject_prefix, var, value);
+	if (!strcmp(var, "format.pretty")) {
+		FREE_AND_NULL(cfg->fmt_pretty);
+		return git_config_string(&cfg->fmt_pretty, var, value);
+	}
+	if (!strcmp(var, "format.subjectprefix")) {
+		FREE_AND_NULL(cfg->fmt_patch_subject_prefix);
+		return git_config_string(&cfg->fmt_patch_subject_prefix, var, value);
+	}
 	if (!strcmp(var, "format.filenamemaxlength")) {
-		fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
+		cfg->fmt_patch_name_max = git_config_int(var, value, ctx->kvi);
 		return 0;
 	}
 	if (!strcmp(var, "format.encodeemailheaders")) {
-		default_encode_email_headers = git_config_bool(var, value);
+		cfg->default_encode_email_headers = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "log.abbrevcommit")) {
-		default_abbrev_commit = git_config_bool(var, value);
+		cfg->default_abbrev_commit = git_config_bool(var, value);
 		return 0;
 	}
-	if (!strcmp(var, "log.date"))
-		return git_config_string(&default_date_mode, var, value);
+	if (!strcmp(var, "log.date")) {
+		FREE_AND_NULL(cfg->default_date_mode);
+		return git_config_string(&cfg->default_date_mode, var, value);
+	}
 	if (!strcmp(var, "log.decorate")) {
-		decoration_style = parse_decoration_style(value);
-		if (decoration_style < 0)
-			decoration_style = 0; /* maybe warn? */
+		cfg->decoration_style = parse_decoration_style(value);
+		if (cfg->decoration_style < 0)
+			cfg->decoration_style = 0; /* maybe warn? */
 		return 0;
 	}
 	if (!strcmp(var, "log.diffmerges")) {
@@ -598,21 +616,21 @@
 		return diff_merges_config(value);
 	}
 	if (!strcmp(var, "log.showroot")) {
-		default_show_root = git_config_bool(var, value);
+		cfg->default_show_root = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "log.follow")) {
-		default_follow = git_config_bool(var, value);
+		cfg->default_follow = git_config_bool(var, value);
 		return 0;
 	}
 	if (skip_prefix(var, "color.decorate.", &slot_name))
 		return parse_decorate_color_config(var, slot_name, value);
 	if (!strcmp(var, "log.mailmap")) {
-		use_mailmap_config = git_config_bool(var, value);
+		cfg->use_mailmap_config = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "log.showsignature")) {
-		default_show_signature = git_config_bool(var, value);
+		cfg->default_show_signature = git_config_bool(var, value);
 		return 0;
 	}
 
@@ -621,11 +639,14 @@
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 {
+	struct log_config cfg;
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	int ret;
 
-	init_log_defaults();
-	git_config(git_log_config, NULL);
+	log_config_init(&cfg);
+	init_diff_ui_defaults();
+	git_config(git_log_config, &cfg);
 
 	repo_init_revisions(the_repository, &rev, prefix);
 	git_config(grep_config, &rev.grep_filter);
@@ -635,10 +656,15 @@
 	memset(&opt, 0, sizeof(opt));
 	opt.def = "HEAD";
 	opt.revarg_opt = REVARG_COMMITTISH;
-	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+
+	ret = cmd_log_walk(&rev);
+
+	release_revisions(&rev);
+	log_config_release(&cfg);
+	return ret;
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -733,14 +759,16 @@
 
 int cmd_show(int argc, const char **argv, const char *prefix)
 {
+	struct log_config cfg;
 	struct rev_info rev;
 	unsigned int i;
 	struct setup_revision_opt opt;
 	struct pathspec match_all;
 	int ret = 0;
 
-	init_log_defaults();
-	git_config(git_log_config, NULL);
+	log_config_init(&cfg);
+	init_diff_ui_defaults();
+	git_config(git_log_config, &cfg);
 
 	if (the_repository->gitdir) {
 		prepare_repo_settings(the_repository);
@@ -759,10 +787,14 @@
 	memset(&opt, 0, sizeof(opt));
 	opt.def = "HEAD";
 	opt.tweak = show_setup_revisions_tweak;
-	cmd_log_init(argc, argv, prefix, &rev, &opt);
+	cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
 
-	if (!rev.no_walk)
-		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+	if (!rev.no_walk) {
+		ret = cmd_log_walk(&rev);
+		release_revisions(&rev);
+		log_config_release(&cfg);
+		return ret;
+	}
 
 	rev.diffopt.no_free = 1;
 	for (i = 0; i < rev.pending.nr && !ret; i++) {
@@ -832,8 +864,10 @@
 
 	rev.diffopt.no_free = 0;
 	diff_free(&rev.diffopt);
+	release_revisions(&rev);
+	log_config_release(&cfg);
 
-	return cmd_log_deinit(ret, &rev);
+	return ret;
 }
 
 /*
@@ -841,11 +875,14 @@
  */
 int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 {
+	struct log_config cfg;
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	int ret;
 
-	init_log_defaults();
-	git_config(git_log_config, NULL);
+	log_config_init(&cfg);
+	init_diff_ui_defaults();
+	git_config(git_log_config, &cfg);
 
 	repo_init_revisions(the_repository, &rev, prefix);
 	init_reflog_walk(&rev.reflog_info);
@@ -854,14 +891,18 @@
 	rev.verbose_header = 1;
 	memset(&opt, 0, sizeof(opt));
 	opt.def = "HEAD";
-	cmd_log_init_defaults(&rev);
+	cmd_log_init_defaults(&rev, &cfg);
 	rev.abbrev_commit = 1;
 	rev.commit_format = CMIT_FMT_ONELINE;
 	rev.use_terminator = 1;
 	rev.always_show_header = 1;
-	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
+	cmd_log_init_finish(argc, argv, prefix, &rev, &opt, &cfg);
 
-	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+	ret = cmd_log_walk(&rev);
+
+	release_revisions(&rev);
+	log_config_release(&cfg);
+	return ret;
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev)
@@ -876,11 +917,14 @@
 
 int cmd_log(int argc, const char **argv, const char *prefix)
 {
+	struct log_config cfg;
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	int ret;
 
-	init_log_defaults();
-	git_config(git_log_config, NULL);
+	log_config_init(&cfg);
+	init_diff_ui_defaults();
+	git_config(git_log_config, &cfg);
 
 	repo_init_revisions(the_repository, &rev, prefix);
 	git_config(grep_config, &rev.grep_filter);
@@ -890,42 +934,17 @@
 	opt.def = "HEAD";
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
-	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
+	cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg);
+
+	ret = cmd_log_walk(&rev);
+
+	release_revisions(&rev);
+	log_config_release(&cfg);
+	return ret;
 }
 
 /* format-patch */
 
-static const char *fmt_patch_suffix = ".patch";
-static int numbered = 0;
-static int auto_number = 1;
-
-static char *default_attach = NULL;
-
-static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
-static struct string_list extra_to = STRING_LIST_INIT_NODUP;
-static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
-
-static void add_header(const char *value)
-{
-	struct string_list_item *item;
-	int len = strlen(value);
-	while (len && value[len - 1] == '\n')
-		len--;
-
-	if (!strncasecmp(value, "to: ", 4)) {
-		item = string_list_append(&extra_to, value + 4);
-		len -= 4;
-	} else if (!strncasecmp(value, "cc: ", 4)) {
-		item = string_list_append(&extra_cc, value + 4);
-		len -= 4;
-	} else {
-		item = string_list_append(&extra_hdr, value);
-	}
-
-	item->string[len] = '\0';
-}
-
 enum cover_setting {
 	COVER_UNSET,
 	COVER_OFF,
@@ -952,17 +971,61 @@
 	AUTO_BASE_WHEN_ABLE
 };
 
-static enum thread_level thread;
-static int do_signoff;
-static enum auto_base_setting auto_base;
-static char *from;
-static const char *signature = git_version_string;
-static const char *signature_file;
-static enum cover_setting config_cover_letter;
-static const char *config_output_directory;
-static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
-static int show_notes;
-static struct display_notes_opt notes_opt;
+struct format_config {
+	struct log_config log;
+	enum thread_level thread;
+	int do_signoff;
+	enum auto_base_setting auto_base;
+	char *base_commit;
+	char *from;
+	char *signature;
+	char *signature_file;
+	enum cover_setting config_cover_letter;
+	char *config_output_directory;
+	enum cover_from_description cover_from_description_mode;
+	int show_notes;
+	struct display_notes_opt notes_opt;
+	int numbered_cmdline_opt;
+	int numbered;
+	int auto_number;
+	char *default_attach;
+	struct string_list extra_hdr;
+	struct string_list extra_to;
+	struct string_list extra_cc;
+	int keep_subject;
+	int subject_prefix;
+	struct strbuf sprefix;
+	char *fmt_patch_suffix;
+};
+
+static void format_config_init(struct format_config *cfg)
+{
+	memset(cfg, 0, sizeof(*cfg));
+	log_config_init(&cfg->log);
+	cfg->cover_from_description_mode = COVER_FROM_MESSAGE;
+	cfg->auto_number = 1;
+	string_list_init_dup(&cfg->extra_hdr);
+	string_list_init_dup(&cfg->extra_to);
+	string_list_init_dup(&cfg->extra_cc);
+	strbuf_init(&cfg->sprefix, 0);
+	cfg->fmt_patch_suffix = xstrdup(".patch");
+}
+
+static void format_config_release(struct format_config *cfg)
+{
+	log_config_release(&cfg->log);
+	free(cfg->base_commit);
+	free(cfg->from);
+	free(cfg->signature);
+	free(cfg->signature_file);
+	free(cfg->config_output_directory);
+	free(cfg->default_attach);
+	string_list_clear(&cfg->extra_hdr, 0);
+	string_list_clear(&cfg->extra_to, 0);
+	string_list_clear(&cfg->extra_cc, 0);
+	strbuf_release(&cfg->sprefix);
+	free(cfg->fmt_patch_suffix);
+}
 
 static enum cover_from_description parse_cover_from_description(const char *arg)
 {
@@ -980,27 +1043,51 @@
 		die(_("%s: invalid cover from description mode"), arg);
 }
 
+static void add_header(struct format_config *cfg, const char *value)
+{
+	struct string_list_item *item;
+	int len = strlen(value);
+	while (len && value[len - 1] == '\n')
+		len--;
+
+	if (!strncasecmp(value, "to: ", 4)) {
+		item = string_list_append(&cfg->extra_to, value + 4);
+		len -= 4;
+	} else if (!strncasecmp(value, "cc: ", 4)) {
+		item = string_list_append(&cfg->extra_cc, value + 4);
+		len -= 4;
+	} else {
+		item = string_list_append(&cfg->extra_hdr, value);
+	}
+
+	item->string[len] = '\0';
+}
+
 static int git_format_config(const char *var, const char *value,
 			     const struct config_context *ctx, void *cb)
 {
+	struct format_config *cfg = cb;
+
 	if (!strcmp(var, "format.headers")) {
 		if (!value)
 			die(_("format.headers without value"));
-		add_header(value);
+		add_header(cfg, value);
 		return 0;
 	}
-	if (!strcmp(var, "format.suffix"))
-		return git_config_string(&fmt_patch_suffix, var, value);
+	if (!strcmp(var, "format.suffix")) {
+		FREE_AND_NULL(cfg->fmt_patch_suffix);
+		return git_config_string(&cfg->fmt_patch_suffix, var, value);
+	}
 	if (!strcmp(var, "format.to")) {
 		if (!value)
 			return config_error_nonbool(var);
-		string_list_append(&extra_to, value);
+		string_list_append(&cfg->extra_to, value);
 		return 0;
 	}
 	if (!strcmp(var, "format.cc")) {
 		if (!value)
 			return config_error_nonbool(var);
-		string_list_append(&extra_cc, value);
+		string_list_append(&cfg->extra_cc, value);
 		return 0;
 	}
 	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") ||
@@ -1009,69 +1096,76 @@
 	}
 	if (!strcmp(var, "format.numbered")) {
 		if (value && !strcasecmp(value, "auto")) {
-			auto_number = 1;
+			cfg->auto_number = 1;
 			return 0;
 		}
-		numbered = git_config_bool(var, value);
-		auto_number = auto_number && numbered;
+		cfg->numbered = git_config_bool(var, value);
+		cfg->auto_number = cfg->auto_number && cfg->numbered;
 		return 0;
 	}
 	if (!strcmp(var, "format.attach")) {
-		if (value && *value)
-			default_attach = xstrdup(value);
-		else if (value && !*value)
-			FREE_AND_NULL(default_attach);
-		else
-			default_attach = xstrdup(git_version_string);
+		if (value && *value) {
+			FREE_AND_NULL(cfg->default_attach);
+			cfg->default_attach = xstrdup(value);
+		} else if (value && !*value) {
+			FREE_AND_NULL(cfg->default_attach);
+		} else {
+			FREE_AND_NULL(cfg->default_attach);
+			cfg->default_attach = xstrdup(git_version_string);
+		}
 		return 0;
 	}
 	if (!strcmp(var, "format.thread")) {
 		if (value && !strcasecmp(value, "deep")) {
-			thread = THREAD_DEEP;
+			cfg->thread = THREAD_DEEP;
 			return 0;
 		}
 		if (value && !strcasecmp(value, "shallow")) {
-			thread = THREAD_SHALLOW;
+			cfg->thread = THREAD_SHALLOW;
 			return 0;
 		}
-		thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
+		cfg->thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
 		return 0;
 	}
 	if (!strcmp(var, "format.signoff")) {
-		do_signoff = git_config_bool(var, value);
+		cfg->do_signoff = git_config_bool(var, value);
 		return 0;
 	}
-	if (!strcmp(var, "format.signature"))
-		return git_config_string(&signature, var, value);
-	if (!strcmp(var, "format.signaturefile"))
-		return git_config_pathname(&signature_file, var, value);
+	if (!strcmp(var, "format.signature")) {
+		FREE_AND_NULL(cfg->signature);
+		return git_config_string(&cfg->signature, var, value);
+	}
+	if (!strcmp(var, "format.signaturefile")) {
+		FREE_AND_NULL(cfg->signature_file);
+		return git_config_pathname(&cfg->signature_file, var, value);
+	}
 	if (!strcmp(var, "format.coverletter")) {
 		if (value && !strcasecmp(value, "auto")) {
-			config_cover_letter = COVER_AUTO;
+			cfg->config_cover_letter = COVER_AUTO;
 			return 0;
 		}
-		config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
+		cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
 		return 0;
 	}
-	if (!strcmp(var, "format.outputdirectory"))
-		return git_config_string(&config_output_directory, var, value);
+	if (!strcmp(var, "format.outputdirectory")) {
+		FREE_AND_NULL(cfg->config_output_directory);
+		return git_config_string(&cfg->config_output_directory, var, value);
+	}
 	if (!strcmp(var, "format.useautobase")) {
 		if (value && !strcasecmp(value, "whenAble")) {
-			auto_base = AUTO_BASE_WHEN_ABLE;
+			cfg->auto_base = AUTO_BASE_WHEN_ABLE;
 			return 0;
 		}
-		auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
+		cfg->auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
 		return 0;
 	}
 	if (!strcmp(var, "format.from")) {
 		int b = git_parse_maybe_bool(value);
-		free(from);
+		FREE_AND_NULL(cfg->from);
 		if (b < 0)
-			from = xstrdup(value);
+			cfg->from = xstrdup(value);
 		else if (b)
-			from = xstrdup(git_committer_info(IDENT_NO_DATE));
-		else
-			from = NULL;
+			cfg->from = xstrdup(git_committer_info(IDENT_NO_DATE));
 		return 0;
 	}
 	if (!strcmp(var, "format.forceinbodyfrom")) {
@@ -1081,15 +1175,15 @@
 	if (!strcmp(var, "format.notes")) {
 		int b = git_parse_maybe_bool(value);
 		if (b < 0)
-			enable_ref_display_notes(&notes_opt, &show_notes, value);
+			enable_ref_display_notes(&cfg->notes_opt, &cfg->show_notes, value);
 		else if (b)
-			enable_default_display_notes(&notes_opt, &show_notes);
+			enable_default_display_notes(&cfg->notes_opt, &cfg->show_notes);
 		else
-			disable_display_notes(&notes_opt, &show_notes);
+			disable_display_notes(&cfg->notes_opt, &cfg->show_notes);
 		return 0;
 	}
 	if (!strcmp(var, "format.coverfromdescription")) {
-		cover_from_description_mode = parse_cover_from_description(value);
+		cfg->cover_from_description_mode = parse_cover_from_description(value);
 		return 0;
 	}
 	if (!strcmp(var, "format.mboxrd")) {
@@ -1110,7 +1204,7 @@
 	if (!strcmp(var, "diff.noprefix"))
 		return 0;
 
-	return git_log_config(var, value, ctx, cb);
+	return git_log_config(var, value, ctx, &cfg->log);
 }
 
 static const char *output_directory = NULL;
@@ -1198,7 +1292,7 @@
 	info->message_id = strbuf_detach(&buf, NULL);
 }
 
-static void print_signature(FILE *file)
+static void print_signature(const char *signature, FILE *file)
 {
 	if (!signature || !*signature)
 		return;
@@ -1268,14 +1362,15 @@
 			       const char *branch_name,
 			       struct strbuf *sb,
 			       const char *encoding,
-			       int need_8bit_cte)
+			       int need_8bit_cte,
+			       const struct format_config *cfg)
 {
 	const char *subject = "*** SUBJECT HERE ***";
 	const char *body = "*** BLURB HERE ***";
 	struct strbuf description_sb = STRBUF_INIT;
 	struct strbuf subject_sb = STRBUF_INIT;
 
-	if (cover_from_description_mode == COVER_FROM_NONE)
+	if (cfg->cover_from_description_mode == COVER_FROM_NONE)
 		goto do_pp;
 
 	if (description_file && *description_file)
@@ -1285,13 +1380,13 @@
 	if (!description_sb.len)
 		goto do_pp;
 
-	if (cover_from_description_mode == COVER_FROM_SUBJECT ||
-			cover_from_description_mode == COVER_FROM_AUTO)
+	if (cfg->cover_from_description_mode == COVER_FROM_SUBJECT ||
+	    cfg->cover_from_description_mode == COVER_FROM_AUTO)
 		body = format_subject(&subject_sb, description_sb.buf, " ");
 
-	if (cover_from_description_mode == COVER_FROM_MESSAGE ||
-			(cover_from_description_mode == COVER_FROM_AUTO &&
-			 subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+	if (cfg->cover_from_description_mode == COVER_FROM_MESSAGE ||
+	    (cfg->cover_from_description_mode == COVER_FROM_AUTO &&
+	     subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
 		body = description_sb.buf;
 	else
 		subject = subject_sb.buf;
@@ -1328,7 +1423,8 @@
 			      int nr, struct commit **list,
 			      const char *description_file,
 			      const char *branch_name,
-			      int quiet)
+			      int quiet,
+			      const struct format_config *cfg)
 {
 	const char *committer;
 	struct shortlog log;
@@ -1367,7 +1463,7 @@
 	pp.encode_email_headers = rev->encode_email_headers;
 	pp_user_info(&pp, NULL, &sb, committer, encoding);
 	prepare_cover_text(&pp, description_file, branch_name, &sb,
-			   encoding, need_8bit_cte);
+			   encoding, need_8bit_cte, cfg);
 	fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
 	free(pp.after_subject);
@@ -1468,41 +1564,54 @@
 	NULL
 };
 
-static int keep_subject = 0;
+struct keep_callback_data {
+	struct format_config *cfg;
+	struct rev_info *revs;
+};
 
 static int keep_callback(const struct option *opt, const char *arg, int unset)
 {
+	struct keep_callback_data *data = opt->value;
 	BUG_ON_OPT_NEG(unset);
 	BUG_ON_OPT_ARG(arg);
-	((struct rev_info *)opt->value)->total = -1;
-	keep_subject = 1;
+	data->revs->total = -1;
+	data->cfg->keep_subject = 1;
 	return 0;
 }
 
-static int subject_prefix = 0;
-
 static int subject_prefix_callback(const struct option *opt, const char *arg,
 			    int unset)
 {
-	struct strbuf *sprefix;
+	struct format_config *cfg = opt->value;
 
 	BUG_ON_OPT_NEG(unset);
-	sprefix = opt->value;
-	subject_prefix = 1;
-	strbuf_reset(sprefix);
-	strbuf_addstr(sprefix, arg);
+	cfg->subject_prefix = 1;
+	strbuf_reset(&cfg->sprefix);
+	strbuf_addstr(&cfg->sprefix, arg);
 	return 0;
 }
 
-static int numbered_cmdline_opt = 0;
+static int rfc_callback(const struct option *opt, const char *arg,
+			int unset)
+{
+	const char **rfc = opt->value;
+
+	*rfc = opt->value;
+	if (unset)
+		*rfc = NULL;
+	else
+		*rfc = arg ? arg : "RFC";
+	return 0;
+}
 
 static int numbered_callback(const struct option *opt, const char *arg,
 			     int unset)
 {
+	struct format_config *cfg = opt->value;
 	BUG_ON_OPT_ARG(arg);
-	*(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
+	cfg->numbered = cfg->numbered_cmdline_opt = unset ? 0 : 1;
 	if (unset)
-		auto_number =  0;
+		cfg->auto_number =  0;
 	return 0;
 }
 
@@ -1526,13 +1635,14 @@
 
 static int thread_callback(const struct option *opt, const char *arg, int unset)
 {
-	enum thread_level *thread = (enum thread_level *)opt->value;
+	struct format_config *cfg = opt->value;
+
 	if (unset)
-		*thread = THREAD_UNSET;
+		cfg->thread = THREAD_UNSET;
 	else if (!arg || !strcmp(arg, "shallow"))
-		*thread = THREAD_SHALLOW;
+		cfg->thread = THREAD_SHALLOW;
 	else if (!strcmp(arg, "deep"))
-		*thread = THREAD_DEEP;
+		cfg->thread = THREAD_DEEP;
 	/*
 	 * Please update _git_formatpatch() in git-completion.bash
 	 * when you add new options.
@@ -1568,15 +1678,17 @@
 	return 0;
 }
 
-static int header_callback(const struct option *opt UNUSED, const char *arg,
+static int header_callback(const struct option *opt, const char *arg,
 			   int unset)
 {
+	struct format_config *cfg = opt->value;
+
 	if (unset) {
-		string_list_clear(&extra_hdr, 0);
-		string_list_clear(&extra_to, 0);
-		string_list_clear(&extra_cc, 0);
+		string_list_clear(&cfg->extra_hdr, 0);
+		string_list_clear(&cfg->extra_to, 0);
+		string_list_clear(&cfg->extra_cc, 0);
 	} else {
-		add_header(arg);
+		add_header(cfg, arg);
 	}
 	return 0;
 }
@@ -1598,17 +1710,17 @@
 
 static int base_callback(const struct option *opt, const char *arg, int unset)
 {
-	const char **base_commit = opt->value;
+	struct format_config *cfg = opt->value;
 
 	if (unset) {
-		auto_base = AUTO_BASE_NEVER;
-		*base_commit = NULL;
+		cfg->auto_base = AUTO_BASE_NEVER;
+		FREE_AND_NULL(cfg->base_commit);
 	} else if (!strcmp(arg, "auto")) {
-		auto_base = AUTO_BASE_ALWAYS;
-		*base_commit = NULL;
+		cfg->auto_base = AUTO_BASE_ALWAYS;
+		FREE_AND_NULL(cfg->base_commit);
 	} else {
-		auto_base = AUTO_BASE_NEVER;
-		*base_commit = arg;
+		cfg->auto_base = AUTO_BASE_NEVER;
+		cfg->base_commit = xstrdup(arg);
 	}
 	return 0;
 }
@@ -1619,7 +1731,7 @@
 	struct object_id *patch_id;
 };
 
-static struct commit *get_base_commit(const char *base_commit,
+static struct commit *get_base_commit(const struct format_config *cfg,
 				      struct commit **list,
 				      int total)
 {
@@ -1627,9 +1739,9 @@
 	struct commit **rev;
 	int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
 
-	switch (auto_base) {
+	switch (cfg->auto_base) {
 	case AUTO_BASE_NEVER:
-		if (base_commit) {
+		if (cfg->base_commit) {
 			auto_select = 0;
 			die_on_failure = 1;
 		} else {
@@ -1639,11 +1751,11 @@
 		break;
 	case AUTO_BASE_ALWAYS:
 	case AUTO_BASE_WHEN_ABLE:
-		if (base_commit) {
+		if (cfg->base_commit) {
 			BUG("requested automatic base selection but a commit was provided");
 		} else {
 			auto_select = 1;
-			die_on_failure = auto_base == AUTO_BASE_ALWAYS;
+			die_on_failure = cfg->auto_base == AUTO_BASE_ALWAYS;
 		}
 		break;
 	default:
@@ -1651,9 +1763,9 @@
 	}
 
 	if (!auto_select) {
-		base = lookup_commit_reference_by_name(base_commit);
+		base = lookup_commit_reference_by_name(cfg->base_commit);
 		if (!base)
-			die(_("unknown commit %s"), base_commit);
+			die(_("unknown commit %s"), cfg->base_commit);
 	} else {
 		struct branch *curr_branch = branch_get(NULL);
 		const char *upstream = branch_get_upstream(curr_branch, NULL);
@@ -1871,6 +1983,7 @@
 
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
+	struct format_config cfg;
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
@@ -1895,7 +2008,6 @@
 	char *cover_from_description_arg = NULL;
 	char *description_file = NULL;
 	char *branch_name = NULL;
-	char *base_commit = NULL;
 	struct base_tree_info bases;
 	struct commit *base;
 	int show_progress = 0;
@@ -1906,18 +2018,24 @@
 	struct strbuf rdiff1 = STRBUF_INIT;
 	struct strbuf rdiff2 = STRBUF_INIT;
 	struct strbuf rdiff_title = STRBUF_INIT;
-	struct strbuf sprefix = STRBUF_INIT;
+	const char *rfc = NULL;
 	int creation_factor = -1;
-	int rfc = 0;
+	const char *signature = git_version_string;
+	const char *signature_file_arg = NULL;
+	struct keep_callback_data keep_callback_data = {
+		.cfg = &cfg,
+		.revs = &rev,
+	};
+	const char *fmt_patch_suffix = NULL;
 
 	const struct option builtin_format_patch_options[] = {
-		OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
+		OPT_CALLBACK_F('n', "numbered", &cfg, NULL,
 			    N_("use [PATCH n/m] even with a single patch"),
 			    PARSE_OPT_NOARG, numbered_callback),
-		OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL,
+		OPT_CALLBACK_F('N', "no-numbered", &cfg, NULL,
 			    N_("use [PATCH] even with multiple patches"),
 			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback),
-		OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")),
+		OPT_BOOL('s', "signoff", &cfg.do_signoff, N_("add a Signed-off-by trailer")),
 		OPT_BOOL(0, "stdout", &use_stdout,
 			    N_("print patches to standard out")),
 		OPT_BOOL(0, "cover-letter", &cover_letter,
@@ -1930,21 +2048,23 @@
 			    N_("start numbering patches at <n> instead of 1")),
 		OPT_STRING('v', "reroll-count", &reroll_count, N_("reroll-count"),
 			    N_("mark the series as Nth re-roll")),
-		OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max,
+		OPT_INTEGER(0, "filename-max-length", &cfg.log.fmt_patch_name_max,
 			    N_("max length of output filename")),
-		OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")),
+		OPT_CALLBACK_F(0, "rfc", &rfc, N_("rfc"),
+			       N_("add <rfc> (default 'RFC') before 'PATCH'"),
+			       PARSE_OPT_OPTARG, rfc_callback),
 		OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
 			    N_("cover-from-description-mode"),
 			    N_("generate parts of a cover letter based on a branch's description")),
 		OPT_FILENAME(0, "description-file", &description_file,
 			     N_("use branch description from file")),
-		OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"),
+		OPT_CALLBACK_F(0, "subject-prefix", &cfg, N_("prefix"),
 			    N_("use [<prefix>] instead of [PATCH]"),
 			    PARSE_OPT_NONEG, subject_prefix_callback),
 		OPT_CALLBACK_F('o', "output-directory", &output_directory,
 			    N_("dir"), N_("store resulting files in <dir>"),
 			    PARSE_OPT_NONEG, output_directory_callback),
-		OPT_CALLBACK_F('k', "keep-subject", &rev, NULL,
+		OPT_CALLBACK_F('k', "keep-subject", &keep_callback_data, NULL,
 			    N_("don't strip/add [PATCH]"),
 			    PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback),
 		OPT_BOOL(0, "no-binary", &no_binary_diff,
@@ -1957,11 +2077,11 @@
 			      N_("show patch format instead of default (patch + stat)"),
 			      1, PARSE_OPT_NONEG),
 		OPT_GROUP(N_("Messaging")),
-		OPT_CALLBACK(0, "add-header", NULL, N_("header"),
+		OPT_CALLBACK(0, "add-header", &cfg, N_("header"),
 			    N_("add email header"), header_callback),
-		OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")),
-		OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")),
-		OPT_CALLBACK_F(0, "from", &from, N_("ident"),
+		OPT_STRING_LIST(0, "to", &cfg.extra_to, N_("email"), N_("add To: header")),
+		OPT_STRING_LIST(0, "cc", &cfg.extra_cc, N_("email"), N_("add Cc: header")),
+		OPT_CALLBACK_F(0, "from", &cfg.from, N_("ident"),
 			    N_("set From address to <ident> (or committer ident if absent)"),
 			    PARSE_OPT_OPTARG, from_callback),
 		OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
@@ -1973,15 +2093,15 @@
 			    N_("inline the patch"),
 			    PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
 			    inline_callback),
-		OPT_CALLBACK_F(0, "thread", &thread, N_("style"),
+		OPT_CALLBACK_F(0, "thread", &cfg, N_("style"),
 			    N_("enable message threading, styles: shallow, deep"),
 			    PARSE_OPT_OPTARG, thread_callback),
 		OPT_STRING(0, "signature", &signature, N_("signature"),
 			    N_("add a signature")),
-		OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"),
+		OPT_CALLBACK_F(0, "base", &cfg, N_("base-commit"),
 			       N_("add prerequisite tree info to the patch series"),
 			       0, base_callback),
-		OPT_FILENAME(0, "signature-file", &signature_file,
+		OPT_FILENAME(0, "signature-file", &signature_file_arg,
 				N_("add a signature from a file")),
 		OPT__QUIET(&quiet, N_("don't print the patch filenames")),
 		OPT_BOOL(0, "progress", &show_progress,
@@ -1998,20 +2118,17 @@
 		OPT_END()
 	};
 
-	extra_hdr.strdup_strings = 1;
-	extra_to.strdup_strings = 1;
-	extra_cc.strdup_strings = 1;
-
-	init_log_defaults();
-	init_display_notes(&notes_opt);
-	git_config(git_format_config, NULL);
+	format_config_init(&cfg);
+	init_diff_ui_defaults();
+	init_display_notes(&cfg.notes_opt);
+	git_config(git_format_config, &cfg);
 	repo_init_revisions(the_repository, &rev, prefix);
 	git_config(grep_config, &rev.grep_filter);
 
-	rev.show_notes = show_notes;
-	memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
+	rev.show_notes = cfg.show_notes;
+	memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt));
 	rev.commit_format = CMIT_FMT_EMAIL;
-	rev.encode_email_headers = default_encode_email_headers;
+	rev.encode_email_headers = cfg.log.default_encode_email_headers;
 	rev.expand_tabs_in_log_default = 0;
 	rev.verbose_header = 1;
 	rev.diff = 1;
@@ -2022,12 +2139,12 @@
 	s_r_opt.def = "HEAD";
 	s_r_opt.revarg_opt = REVARG_COMMITTISH;
 
-	strbuf_addstr(&sprefix, fmt_patch_subject_prefix);
+	strbuf_addstr(&cfg.sprefix, cfg.log.fmt_patch_subject_prefix);
 	if (format_no_prefix)
 		diff_set_noprefix(&rev.diffopt);
 
-	if (default_attach) {
-		rev.mime_boundary = default_attach;
+	if (cfg.default_attach) {
+		rev.mime_boundary = cfg.default_attach;
 		rev.no_inline = 1;
 	}
 
@@ -2043,55 +2160,63 @@
 
 	rev.force_in_body_from = force_in_body_from;
 
+	if (!fmt_patch_suffix)
+		fmt_patch_suffix = cfg.fmt_patch_suffix;
+
 	/* Make sure "0000-$sub.patch" gives non-negative length for $sub */
-	if (fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
-		fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
+	if (cfg.log.fmt_patch_name_max <= strlen("0000-") + strlen(fmt_patch_suffix))
+		cfg.log.fmt_patch_name_max = strlen("0000-") + strlen(fmt_patch_suffix);
 
 	if (cover_from_description_arg)
-		cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+		cfg.cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
 
-	if (rfc)
-		strbuf_insertstr(&sprefix, 0, "RFC ");
+	if (rfc && rfc[0]) {
+		cfg.subject_prefix = 1;
+		if (rfc[0] == '-')
+			strbuf_addf(&cfg.sprefix, " %s", rfc + 1);
+		else
+			strbuf_insertf(&cfg.sprefix, 0, "%s ", rfc);
+	}
 
 	if (reroll_count) {
-		strbuf_addf(&sprefix, " v%s", reroll_count);
+		strbuf_addf(&cfg.sprefix, " v%s", reroll_count);
 		rev.reroll_count = reroll_count;
 	}
 
-	rev.subject_prefix = sprefix.buf;
+	rev.subject_prefix = cfg.sprefix.buf;
 
-	for (i = 0; i < extra_hdr.nr; i++) {
-		strbuf_addstr(&buf, extra_hdr.items[i].string);
+	for (i = 0; i < cfg.extra_hdr.nr; i++) {
+		strbuf_addstr(&buf, cfg.extra_hdr.items[i].string);
 		strbuf_addch(&buf, '\n');
 	}
 
-	if (extra_to.nr)
+	if (cfg.extra_to.nr)
 		strbuf_addstr(&buf, "To: ");
-	for (i = 0; i < extra_to.nr; i++) {
+	for (i = 0; i < cfg.extra_to.nr; i++) {
 		if (i)
 			strbuf_addstr(&buf, "    ");
-		strbuf_addstr(&buf, extra_to.items[i].string);
-		if (i + 1 < extra_to.nr)
+		strbuf_addstr(&buf, cfg.extra_to.items[i].string);
+		if (i + 1 < cfg.extra_to.nr)
 			strbuf_addch(&buf, ',');
 		strbuf_addch(&buf, '\n');
 	}
 
-	if (extra_cc.nr)
+	if (cfg.extra_cc.nr)
 		strbuf_addstr(&buf, "Cc: ");
-	for (i = 0; i < extra_cc.nr; i++) {
+	for (i = 0; i < cfg.extra_cc.nr; i++) {
 		if (i)
 			strbuf_addstr(&buf, "    ");
-		strbuf_addstr(&buf, extra_cc.items[i].string);
-		if (i + 1 < extra_cc.nr)
+		strbuf_addstr(&buf, cfg.extra_cc.items[i].string);
+		if (i + 1 < cfg.extra_cc.nr)
 			strbuf_addch(&buf, ',');
 		strbuf_addch(&buf, '\n');
 	}
 
 	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
-	if (from) {
-		if (split_ident_line(&rev.from_ident, from, strlen(from)))
-			die(_("invalid ident line: %s"), from);
+	if (cfg.from) {
+		if (split_ident_line(&rev.from_ident, cfg.from, strlen(cfg.from)))
+			die(_("invalid ident line: %s"), cfg.from);
 	}
 
 	if (start_number < 0)
@@ -2102,14 +2227,14 @@
 	 * and it would conflict with --keep-subject (-k) from the
 	 * command line, reset "numbered".
 	 */
-	if (numbered && keep_subject && !numbered_cmdline_opt)
-		numbered = 0;
+	if (cfg.numbered && cfg.keep_subject && !cfg.numbered_cmdline_opt)
+		cfg.numbered = 0;
 
-	if (numbered && keep_subject)
+	if (cfg.numbered && cfg.keep_subject)
 		die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
-	if (keep_subject && subject_prefix)
+	if (cfg.keep_subject && cfg.subject_prefix)
 		die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
-	rev.preserve_subject = keep_subject;
+	rev.preserve_subject = cfg.keep_subject;
 
 	argc = setup_revisions(argc, argv, &rev, &s_r_opt);
 	if (argc > 1)
@@ -2136,7 +2261,7 @@
 	rev.always_show_header = 1;
 
 	rev.zero_commit = zero_commit;
-	rev.patch_name_max = fmt_patch_name_max;
+	rev.patch_name_max = cfg.log.fmt_patch_name_max;
 
 	if (!rev.diffopt.flags.text && !no_binary_diff)
 		rev.diffopt.flags.binary = 1;
@@ -2157,7 +2282,7 @@
 		int saved;
 
 		if (!output_directory)
-			output_directory = config_output_directory;
+			output_directory = cfg.config_output_directory;
 		output_directory = set_outdir(prefix, output_directory);
 
 		if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
@@ -2206,8 +2331,10 @@
 
 		if (check_head) {
 			const char *ref, *v;
-			ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-						 NULL, NULL);
+			ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						      "HEAD",
+						      RESOLVE_REF_READING,
+						      NULL, NULL);
 			if (ref && skip_prefix(ref, "refs/heads/", &v))
 				branch_name = xstrdup(v);
 			else
@@ -2253,14 +2380,14 @@
 		goto done;
 	total = nr;
 	if (cover_letter == -1) {
-		if (config_cover_letter == COVER_AUTO)
+		if (cfg.config_cover_letter == COVER_AUTO)
 			cover_letter = (total > 1);
 		else
-			cover_letter = (config_cover_letter == COVER_ON);
+			cover_letter = (cfg.config_cover_letter == COVER_ON);
 	}
-	if (!keep_subject && auto_number && (total > 1 || cover_letter))
-		numbered = 1;
-	if (numbered)
+	if (!cfg.keep_subject && cfg.auto_number && (total > 1 || cover_letter))
+		cfg.numbered = 1;
+	if (cfg.numbered)
 		rev.total = total + start_number - 1;
 
 	if (idiff_prev.nr) {
@@ -2274,7 +2401,7 @@
 	}
 
 	if (creation_factor < 0)
-		creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
+		creation_factor = CREATION_FACTOR_FOR_THE_SAME_SERIES;
 	else if (!rdiff_prev)
 		die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
 
@@ -2292,27 +2419,40 @@
 					     _("Range-diff against v%d:"));
 	}
 
+	/*
+	 * The order of precedence is:
+	 *
+	 *   1. The `--signature` and `--no-signature` options.
+	 *   2. The `--signature-file` option.
+	 *   3. The `format.signature` config.
+	 *   4. The `format.signatureFile` config.
+	 *   5. Default `git_version_string`.
+	 */
 	if (!signature) {
 		; /* --no-signature inhibits all signatures */
 	} else if (signature && signature != git_version_string) {
 		; /* non-default signature already set */
-	} else if (signature_file) {
+	} else if (signature_file_arg || (cfg.signature_file && !cfg.signature)) {
 		struct strbuf buf = STRBUF_INIT;
+		const char *signature_file = signature_file_arg ?
+			signature_file_arg : cfg.signature_file;
 
 		if (strbuf_read_file(&buf, signature_file, 128) < 0)
 			die_errno(_("unable to read signature file '%s'"), signature_file);
 		signature = strbuf_detach(&buf, NULL);
+	} else if (cfg.signature) {
+		signature = cfg.signature;
 	}
 
 	memset(&bases, 0, sizeof(bases));
-	base = get_base_commit(base_commit, list, nr);
+	base = get_base_commit(&cfg, list, nr);
 	if (base) {
 		reset_revision_walk();
 		clear_object_flags(UNINTERESTING);
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter) {
+	if (in_reply_to || cfg.thread || cover_letter) {
 		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
 		string_list_init_dup(rev.ref_message_ids);
 	}
@@ -2323,19 +2463,19 @@
 	rev.numbered_files = just_numbers;
 	rev.patch_suffix = fmt_patch_suffix;
 	if (cover_letter) {
-		if (thread)
+		if (cfg.thread)
 			gen_message_id(&rev, "cover");
 		make_cover_letter(&rev, !!output_directory,
-				  origin, nr, list, description_file, branch_name, quiet);
+				  origin, nr, list, description_file, branch_name, quiet, &cfg);
 		print_bases(&bases, rev.diffopt.file);
-		print_signature(rev.diffopt.file);
+		print_signature(signature, rev.diffopt.file);
 		total++;
 		start_number--;
 		/* interdiff/range-diff in cover-letter; omit from patches */
 		rev.idiff_oid1 = NULL;
 		rev.rdiff1 = NULL;
 	}
-	rev.add_signoff = do_signoff;
+	rev.add_signoff = cfg.do_signoff;
 
 	if (show_progress)
 		progress = start_delayed_progress(_("Generating patches"), total);
@@ -2345,7 +2485,7 @@
 		commit = list[nr];
 		rev.nr = total - nr + (start_number - 1);
 		/* Make the second and subsequent mails replies to the first */
-		if (thread) {
+		if (cfg.thread) {
 			/* Have we already had a message ID? */
 			if (rev.message_id) {
 				/*
@@ -2369,7 +2509,7 @@
 				 * letter is a reply to the
 				 * --in-reply-to, if specified.
 				 */
-				if (thread == THREAD_SHALLOW
+				if (cfg.thread == THREAD_SHALLOW
 				    && rev.ref_message_ids->nr > 0
 				    && (!cover_letter || rev.nr > 1))
 					free(rev.message_id);
@@ -2402,7 +2542,7 @@
 				       mime_boundary_leader,
 				       rev.mime_boundary);
 			else
-				print_signature(rev.diffopt.file);
+				print_signature(signature, rev.diffopt.file);
 		}
 		if (output_directory)
 			fclose(rev.diffopt.file);
@@ -2410,9 +2550,6 @@
 	stop_progress(&progress);
 	free(list);
 	free(branch_name);
-	string_list_clear(&extra_to, 0);
-	string_list_clear(&extra_cc, 0);
-	string_list_clear(&extra_hdr, 0);
 	if (ignore_if_in_upstream)
 		free_patch_ids(&ids);
 
@@ -2422,13 +2559,14 @@
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
-	strbuf_release(&sprefix);
 	free(to_free);
 	free(rev.message_id);
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	return cmd_log_deinit(0, &rev);
+	release_revisions(&rev);
+	format_config_release(&cfg);
+	return 0;
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index 270d5f6..0fabe3f 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "hex.h"
 #include "read-cache-ll.h"
@@ -18,11 +17,11 @@
 	char ownbuf[4][60];
 	struct child_process cmd = CHILD_PROCESS_INIT;
 
-	if (pos >= the_index.cache_nr)
+	if (pos >= the_repository->index->cache_nr)
 		die("git merge-index: %s not in the cache", path);
 	found = 0;
 	do {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		int stage = ce_stage(ce);
 
 		if (strcmp(ce->name, path))
@@ -32,7 +31,7 @@
 		xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode);
 		arguments[stage] = hexbuf[stage];
 		arguments[stage + 4] = ownbuf[stage];
-	} while (++pos < the_index.cache_nr);
+	} while (++pos < the_repository->index->cache_nr);
 	if (!found)
 		die("git merge-index: %s not in the cache", path);
 
@@ -51,7 +50,7 @@
 
 static void merge_one_path(const char *path)
 {
-	int pos = index_name_pos(&the_index, path, strlen(path));
+	int pos = index_name_pos(the_repository->index, path, strlen(path));
 
 	/*
 	 * If it already exists in the cache as stage0, it's
@@ -65,9 +64,9 @@
 {
 	int i;
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	ensure_full_index(the_repository->index);
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 		if (!ce_stage(ce))
 			continue;
 		i += merge_entry(i, ce->name)-1;
@@ -89,7 +88,7 @@
 	repo_read_index(the_repository);
 
 	/* TODO: audit for interaction with sparse-index. */
-	ensure_full_index(&the_index);
+	ensure_full_index(the_repository->index);
 
 	i = 1;
 	if (!strcmp(argv[i], "-o")) {
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8bdb439..1082d91 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "tree-walk.h"
 #include "xdiff-interface.h"
@@ -364,7 +363,7 @@
 
 	setup_traverse_info(&info, base);
 	info.fn = threeway_callback;
-	traverse_trees(&the_index, 3, t, &info);
+	traverse_trees(the_repository->index, 3, t, &info);
 }
 
 static void *get_tree_descriptor(struct repository *r,
diff --git a/builtin/merge.c b/builtin/merge.c
index 6f4fec8..daed2d4 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -6,7 +6,6 @@
  * Based on git-merge.sh by Junio C Hamano.
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -101,7 +100,7 @@
 	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
 };
 
-static const char *pull_twohead, *pull_octopus;
+static char *pull_twohead, *pull_octopus;
 
 enum ff_type {
 	FF_NO,
@@ -111,7 +110,7 @@
 
 static enum ff_type fast_forward = FF_ALLOW;
 
-static const char *cleanup_arg;
+static char *cleanup_arg;
 static enum commit_msg_cleanup_mode cleanup_mode;
 
 static int option_parse_message(const struct option *opt,
@@ -300,7 +299,7 @@
 	int rc = -1;
 
 	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
-	refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
 		repo_update_index_if_able(the_repository, &lock_file);
 	rollback_lock_file(&lock_file);
@@ -372,7 +371,7 @@
 	run_command(&cmd);
 
 refresh_cache:
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	if (repo_read_index(the_repository) < 0)
 		die(_("could not read index"));
 }
@@ -449,8 +448,10 @@
 		if (verbosity >= 0 && !merge_msg.len)
 			printf(_("No merge message -- not updating HEAD\n"));
 		else {
-			update_ref(reflog_message.buf, "HEAD", new_head, head,
-				   0, UPDATE_REFS_DIE_ON_ERR);
+			refs_update_ref(get_main_ref_store(the_repository),
+					reflog_message.buf, "HEAD", new_head,
+					head,
+					0, UPDATE_REFS_DIE_ON_ERR);
 			/*
 			 * We ignore errors in 'gc --auto', since the
 			 * user should see them.
@@ -547,7 +548,7 @@
 		struct strbuf truname = STRBUF_INIT;
 		strbuf_addf(&truname, "refs/heads/%s", remote);
 		strbuf_setlen(&truname, truname.len - len);
-		if (ref_exists(truname.buf)) {
+		if (refs_ref_exists(get_main_ref_store(the_repository), truname.buf)) {
 			strbuf_addf(msg,
 				    "%s\t\tbranch '%s'%s of .\n",
 				    oid_to_hex(&remote_head->object.oid),
@@ -657,8 +658,8 @@
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 2;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.update = 1;
 	opts.verbose_update = 1;
 	opts.trivial_merges_only = 1;
@@ -674,7 +675,7 @@
 	if (!trees[nr_trees++])
 		return -1;
 	opts.fn = threeway_merge;
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		parse_tree(trees[i]);
 		init_tree_desc(t+i, &trees[i]->object.oid,
@@ -687,7 +688,7 @@
 
 static void write_tree_trivial(struct object_id *oid)
 {
-	if (write_index_as_tree(oid, &the_index, get_index_file(), 0, NULL))
+	if (write_index_as_tree(oid, the_repository->index, get_index_file(), 0, NULL))
 		die(_("git write-tree failed to write a tree"));
 }
 
@@ -745,7 +746,7 @@
 			rollback_lock_file(&lock);
 			return 2;
 		}
-		if (write_locked_index(&the_index, &lock,
+		if (write_locked_index(the_repository->index, &lock,
 				       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 			die(_("unable to write %s"), get_index_file());
 		return clean ? 0 : 1;
@@ -768,8 +769,8 @@
 {
 	int i, ret = 0;
 
-	for (i = 0; i < the_index.cache_nr; i++)
-		if (ce_stage(the_index.cache[i]))
+	for (i = 0; i < the_repository->index->cache_nr; i++)
+		if (ce_stage(the_repository->index->cache[i]))
 			ret++;
 
 	return ret;
@@ -843,9 +844,9 @@
 		 * the editor and after we invoke run_status above.
 		 */
 		if (invoked_hook)
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 	}
-	read_index_from(&the_index, index_file, get_git_dir());
+	read_index_from(the_repository->index, index_file, get_git_dir());
 	strbuf_addbuf(&msg, &merge_msg);
 	if (squash)
 		BUG("the control must not reach here under --squash");
@@ -957,7 +958,7 @@
 	 * Thus, we will get the cleanup mode which is returned when we _are_
 	 * using an editor.
 	 */
-	append_conflicts_hint(&the_index, &msgbuf,
+	append_conflicts_hint(the_repository->index, &msgbuf,
 			      get_cleanup_mode(cleanup_arg, 1));
 	fputs(msgbuf.buf, fp);
 	strbuf_release(&msgbuf);
@@ -1252,7 +1253,7 @@
 	 */
 	tag_ref = xstrfmt("refs/tags/%s",
 			  ((struct tag *)merge_remote_util(commit)->obj)->tag);
-	if (!read_ref(tag_ref, &oid) &&
+	if (!refs_read_ref(get_main_ref_store(the_repository), tag_ref, &oid) &&
 	    oideq(&oid, &merge_remote_util(commit)->obj->oid))
 		is_throwaway_tag = 0;
 	else
@@ -1284,7 +1285,9 @@
 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
 	 * current branch.
 	 */
-	branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	branch = branch_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+						      "HEAD", 0, &head_oid,
+						      NULL);
 	if (branch)
 		skip_prefix(branch, "refs/heads/", &branch);
 
@@ -1325,8 +1328,10 @@
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
-		if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
-			delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
+		if (!refs_read_ref(get_main_ref_store(the_repository), "MERGE_AUTOSTASH", &stash_oid))
+			refs_delete_ref(get_main_ref_store(the_repository),
+					"", "MERGE_AUTOSTASH", &stash_oid,
+					REF_NO_DEREF);
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
@@ -1379,14 +1384,14 @@
 		else
 			die(_("You have not concluded your merge (MERGE_HEAD exists)."));
 	}
-	if (ref_exists("CHERRY_PICK_HEAD")) {
+	if (refs_ref_exists(get_main_ref_store(the_repository), "CHERRY_PICK_HEAD")) {
 		if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 			    "Please, commit your changes before you merge."));
 		else
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	if (option_edit < 0)
 		option_edit = default_edit_option();
@@ -1450,8 +1455,10 @@
 
 		remote_head_oid = &remoteheads->item->object.oid;
 		read_empty(remote_head_oid);
-		update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"initial pull", "HEAD", remote_head_oid, NULL,
+				0,
+				UPDATE_REFS_DIE_ON_ERR);
 		goto done;
 	}
 
@@ -1531,8 +1538,10 @@
 		free(list);
 	}
 
-	update_ref("updating ORIG_HEAD", "ORIG_HEAD",
-		   &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository),
+			"updating ORIG_HEAD", "ORIG_HEAD",
+			&head_commit->object.oid, NULL, 0,
+			UPDATE_REFS_DIE_ON_ERR);
 
 	if (remoteheads && !common) {
 		/* No common ancestors found. */
@@ -1595,7 +1604,7 @@
 		 * We are not doing octopus, not fast-forward, and have
 		 * only one common.
 		 */
-		refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL);
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 		if (allow_trivial && fast_forward != FF_ONLY) {
 			/*
 			 * Must first ensure that index matches HEAD before
@@ -1784,6 +1793,6 @@
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 	return ret;
 }
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index a72aebe..8360932 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -8,6 +8,7 @@
 #include "strbuf.h"
 #include "trace2.h"
 #include "object-store-ll.h"
+#include "replace-object.h"
 
 #define BUILTIN_MIDX_WRITE_USAGE \
 	N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
@@ -273,6 +274,8 @@
 	};
 	struct option *options = parse_options_concat(builtin_multi_pack_index_options, common_opts);
 
+	disable_replace_refs();
+
 	git_config(git_default_config, NULL);
 
 	if (the_repository &&
diff --git a/builtin/mv.c b/builtin/mv.c
index 22e64fc..6c69033 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "advice.h"
@@ -20,6 +20,7 @@
 #include "read-cache-ll.h"
 #include "repository.h"
 #include "setup.h"
+#include "strvec.h"
 #include "submodule.h"
 #include "entry.h"
 
@@ -38,45 +39,35 @@
 #define DUP_BASENAME 1
 #define KEEP_TRAILING_SLASH 2
 
-static const char **internal_prefix_pathspec(const char *prefix,
-					     const char **pathspec,
-					     int count, unsigned flags)
+static void internal_prefix_pathspec(struct strvec *out,
+				     const char *prefix,
+				     const char **pathspec,
+				     int count, unsigned flags)
 {
-	int i;
-	const char **result;
 	int prefixlen = prefix ? strlen(prefix) : 0;
-	ALLOC_ARRAY(result, count + 1);
 
 	/* Create an intermediate copy of the pathspec based on the flags */
-	for (i = 0; i < count; i++) {
-		int length = strlen(pathspec[i]);
-		int to_copy = length;
-		char *it;
+	for (int i = 0; i < count; i++) {
+		size_t length = strlen(pathspec[i]);
+		size_t to_copy = length;
+		const char *maybe_basename;
+		char *trimmed, *prefixed_path;
+
 		while (!(flags & KEEP_TRAILING_SLASH) &&
 		       to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1]))
 			to_copy--;
 
-		it = xmemdupz(pathspec[i], to_copy);
-		if (flags & DUP_BASENAME) {
-			result[i] = xstrdup(basename(it));
-			free(it);
-		} else {
-			result[i] = it;
-		}
-	}
-	result[count] = NULL;
+		trimmed = xmemdupz(pathspec[i], to_copy);
+		maybe_basename = (flags & DUP_BASENAME) ? basename(trimmed) : trimmed;
+		prefixed_path = prefix_path(prefix, prefixlen, maybe_basename);
+		strvec_push(out, prefixed_path);
 
-	/* Prefix the pathspec and free the old intermediate strings */
-	for (i = 0; i < count; i++) {
-		const char *match = prefix_path(prefix, prefixlen, result[i]);
-		free((char *) result[i]);
-		result[i] = match;
+		free(prefixed_path);
+		free(trimmed);
 	}
-
-	return result;
 }
 
-static const char *add_slash(const char *path)
+static char *add_slash(const char *path)
 {
 	size_t len = strlen(path);
 	if (len && path[len - 1] != '/') {
@@ -86,46 +77,48 @@
 		with_slash[len] = 0;
 		return with_slash;
 	}
-	return path;
+	return xstrdup(path);
 }
 
 #define SUBMODULE_WITH_GITDIR ((const char *)1)
 
-static void prepare_move_submodule(const char *src, int first,
-				   const char **submodule_gitfile)
+static const char *submodule_gitfile_path(const char *src, int first)
 {
 	struct strbuf submodule_dotgit = STRBUF_INIT;
-	if (!S_ISGITLINK(the_index.cache[first]->ce_mode))
+	const char *path;
+
+	if (!S_ISGITLINK(the_repository->index->cache[first]->ce_mode))
 		die(_("Directory %s is in index and no submodule?"), src);
-	if (!is_staging_gitmodules_ok(&the_index))
+	if (!is_staging_gitmodules_ok(the_repository->index))
 		die(_("Please stage your changes to .gitmodules or stash them to proceed"));
+
 	strbuf_addf(&submodule_dotgit, "%s/.git", src);
-	*submodule_gitfile = read_gitfile(submodule_dotgit.buf);
-	if (*submodule_gitfile)
-		*submodule_gitfile = xstrdup(*submodule_gitfile);
-	else
-		*submodule_gitfile = SUBMODULE_WITH_GITDIR;
+
+	path = read_gitfile(submodule_dotgit.buf);
 	strbuf_release(&submodule_dotgit);
+	if (path)
+		return path;
+	return SUBMODULE_WITH_GITDIR;
 }
 
 static int index_range_of_same_dir(const char *src, int length,
 				   int *first_p, int *last_p)
 {
-	const char *src_w_slash = add_slash(src);
+	char *src_w_slash = add_slash(src);
 	int first, last, len_w_slash = length + 1;
 
-	first = index_name_pos(&the_index, src_w_slash, len_w_slash);
+	first = index_name_pos(the_repository->index, src_w_slash, len_w_slash);
 	if (first >= 0)
 		die(_("%.*s is in index"), len_w_slash, src_w_slash);
 
 	first = -1 - first;
-	for (last = first; last < the_index.cache_nr; last++) {
-		const char *path = the_index.cache[last]->name;
+	for (last = first; last < the_repository->index->cache_nr; last++) {
+		const char *path = the_repository->index->cache[last]->name;
 		if (strncmp(path, src_w_slash, len_w_slash))
 			break;
 	}
-	if (src_w_slash != src)
-		free((char *)src_w_slash);
+
+	free(src_w_slash);
 	*first_p = first;
 	*last_p = last;
 	return last - first;
@@ -141,17 +134,17 @@
 static int empty_dir_has_sparse_contents(const char *name)
 {
 	int ret = 0;
-	const char *with_slash = add_slash(name);
+	char *with_slash = add_slash(name);
 	int length = strlen(with_slash);
 
-	int pos = index_name_pos(&the_index, with_slash, length);
+	int pos = index_name_pos(the_repository->index, with_slash, length);
 	const struct cache_entry *ce;
 
 	if (pos < 0) {
 		pos = -pos - 1;
-		if (pos >= the_index.cache_nr)
+		if (pos >= the_repository->index->cache_nr)
 			goto free_return;
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 		if (strncmp(with_slash, ce->name, length))
 			goto free_return;
 		if (ce_skip_worktree(ce))
@@ -159,11 +152,32 @@
 	}
 
 free_return:
-	if (with_slash != name)
-		free((char *)with_slash);
+	free(with_slash);
 	return ret;
 }
 
+static void remove_empty_src_dirs(const char **src_dir, size_t src_dir_nr)
+{
+	size_t i;
+	struct strbuf a_src_dir = STRBUF_INIT;
+
+	for (i = 0; i < src_dir_nr; i++) {
+		int dummy;
+		strbuf_addstr(&a_src_dir, src_dir[i]);
+		/*
+		 * if entries under a_src_dir are all moved away,
+		 * recursively remove a_src_dir to cleanup
+		 */
+		if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
+					    &dummy, &dummy) < 1) {
+			remove_dir_recursively(&a_src_dir, 0);
+		}
+		strbuf_reset(&a_src_dir);
+	}
+
+	strbuf_release(&a_src_dir);
+}
+
 int cmd_mv(int argc, const char **argv, const char *prefix)
 {
 	int i, flags, gitmodules_modified = 0;
@@ -177,18 +191,21 @@
 		OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
 		OPT_END(),
 	};
-	const char **source, **destination, **dest_path, **submodule_gitfile;
-	const char *dst_w_slash;
-	const char **src_dir = NULL;
-	int src_dir_nr = 0, src_dir_alloc = 0;
-	struct strbuf a_src_dir = STRBUF_INIT;
+	struct strvec sources = STRVEC_INIT;
+	struct strvec dest_paths = STRVEC_INIT;
+	struct strvec destinations = STRVEC_INIT;
+	struct strvec submodule_gitfiles_to_free = STRVEC_INIT;
+	const char **submodule_gitfiles;
+	char *dst_w_slash = NULL;
+	struct strvec src_dir = STRVEC_INIT;
 	enum update_mode *modes, dst_mode = 0;
 	struct stat st, dest_st;
-	struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
+	struct string_list src_for_dst = STRING_LIST_INIT_DUP;
 	struct lock_file lock_file = LOCK_INIT;
 	struct cache_entry *ce;
-	struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
-	struct string_list dirty_paths = STRING_LIST_INIT_NODUP;
+	struct string_list only_match_skip_worktree = STRING_LIST_INIT_DUP;
+	struct string_list dirty_paths = STRING_LIST_INIT_DUP;
+	int ret;
 
 	git_config(git_default_config, NULL);
 
@@ -201,7 +218,7 @@
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	source = internal_prefix_pathspec(prefix, argv, argc, 0);
+	internal_prefix_pathspec(&sources, prefix, argv, argc, 0);
 	CALLOC_ARRAY(modes, argc);
 
 	/*
@@ -212,45 +229,39 @@
 	flags = KEEP_TRAILING_SLASH;
 	if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1]))
 		flags = 0;
-	dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags);
-	dst_w_slash = add_slash(dest_path[0]);
-	submodule_gitfile = xcalloc(argc, sizeof(char *));
+	internal_prefix_pathspec(&dest_paths, prefix, argv + argc, 1, flags);
+	dst_w_slash = add_slash(dest_paths.v[0]);
+	submodule_gitfiles = xcalloc(argc, sizeof(char *));
 
-	if (dest_path[0][0] == '\0')
+	if (dest_paths.v[0][0] == '\0')
 		/* special case: "." was normalized to "" */
-		destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME);
-	else if (!lstat(dest_path[0], &st) &&
-			S_ISDIR(st.st_mode)) {
-		destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
+		internal_prefix_pathspec(&destinations, dest_paths.v[0], argv, argc, DUP_BASENAME);
+	else if (!lstat(dest_paths.v[0], &st) && S_ISDIR(st.st_mode)) {
+		internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+	} else if (!path_in_sparse_checkout(dst_w_slash, the_repository->index) &&
+		   empty_dir_has_sparse_contents(dst_w_slash)) {
+		internal_prefix_pathspec(&destinations, dst_w_slash, argv, argc, DUP_BASENAME);
+		dst_mode = SKIP_WORKTREE_DIR;
+	} else if (argc != 1) {
+		die(_("destination '%s' is not a directory"), dest_paths.v[0]);
 	} else {
-		if (!path_in_sparse_checkout(dst_w_slash, &the_index) &&
-		    empty_dir_has_sparse_contents(dst_w_slash)) {
-			destination = internal_prefix_pathspec(dst_w_slash, argv, argc, DUP_BASENAME);
-			dst_mode = SKIP_WORKTREE_DIR;
-		} else if (argc != 1) {
-			die(_("destination '%s' is not a directory"), dest_path[0]);
-		} else {
-			destination = dest_path;
-			/*
-			 * <destination> is a file outside of sparse-checkout
-			 * cone. Insist on cone mode here for backward
-			 * compatibility. We don't want dst_mode to be assigned
-			 * for a file when the repo is using no-cone mode (which
-			 * is deprecated at this point) sparse-checkout. As
-			 * SPARSE here is only considering cone-mode situation.
-			 */
-			if (!path_in_cone_mode_sparse_checkout(destination[0], &the_index))
-				dst_mode = SPARSE;
-		}
-	}
-	if (dst_w_slash != dest_path[0]) {
-		free((char *)dst_w_slash);
-		dst_w_slash = NULL;
+		strvec_pushv(&destinations, dest_paths.v);
+
+		/*
+		 * <destination> is a file outside of sparse-checkout
+		 * cone. Insist on cone mode here for backward
+		 * compatibility. We don't want dst_mode to be assigned
+		 * for a file when the repo is using no-cone mode (which
+		 * is deprecated at this point) sparse-checkout. As
+		 * SPARSE here is only considering cone-mode situation.
+		 */
+		if (!path_in_cone_mode_sparse_checkout(destinations.v[0], the_repository->index))
+			dst_mode = SPARSE;
 	}
 
 	/* Checking */
 	for (i = 0; i < argc; i++) {
-		const char *src = source[i], *dst = destination[i];
+		const char *src = sources.v[i], *dst = destinations.v[i];
 		int length;
 		const char *bad = NULL;
 		int skip_sparse = 0;
@@ -263,20 +274,22 @@
 			int pos;
 			const struct cache_entry *ce;
 
-			pos = index_name_pos(&the_index, src, length);
+			pos = index_name_pos(the_repository->index, src, length);
 			if (pos < 0) {
-				const char *src_w_slash = add_slash(src);
-				if (!path_in_sparse_checkout(src_w_slash, &the_index) &&
+				char *src_w_slash = add_slash(src);
+				if (!path_in_sparse_checkout(src_w_slash, the_repository->index) &&
 				    empty_dir_has_sparse_contents(src)) {
+					free(src_w_slash);
 					modes[i] |= SKIP_WORKTREE_DIR;
 					goto dir_check;
 				}
+				free(src_w_slash);
 				/* only error if existence is expected. */
 				if (!(modes[i] & SPARSE))
 					bad = _("bad source");
 				goto act_on_entry;
 			}
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!ce_skip_worktree(ce)) {
 				bad = _("bad source");
 				goto act_on_entry;
@@ -286,7 +299,7 @@
 				goto act_on_entry;
 			}
 			/* Check if dst exists in index */
-			if (index_name_pos(&the_index, dst, strlen(dst)) < 0) {
+			if (index_name_pos(the_repository->index, dst, strlen(dst)) < 0) {
 				modes[i] |= SPARSE;
 				goto act_on_entry;
 			}
@@ -310,12 +323,16 @@
 
 dir_check:
 		if (S_ISDIR(st.st_mode)) {
-			int j, dst_len, n;
-			int first = index_name_pos(&the_index, src, length), last;
+			char *dst_with_slash;
+			size_t dst_with_slash_len;
+			int j, n;
+			int first = index_name_pos(the_repository->index, src, length), last;
 
 			if (first >= 0) {
-				prepare_move_submodule(src, first,
-						       submodule_gitfile + i);
+				const char *path = submodule_gitfile_path(src, first);
+				if (path != SUBMODULE_WITH_GITDIR)
+					path = strvec_push(&submodule_gitfiles_to_free, path);
+				submodule_gitfiles[i] = path;
 				goto act_on_entry;
 			} else if (index_range_of_same_dir(src, length,
 							   &first, &last) < 1) {
@@ -326,32 +343,35 @@
 			/* last - first >= 1 */
 			modes[i] |= WORKING_DIRECTORY;
 
-			ALLOC_GROW(src_dir, src_dir_nr + 1, src_dir_alloc);
-			src_dir[src_dir_nr++] = src;
+			strvec_push(&src_dir, src);
 
 			n = argc + last - first;
-			REALLOC_ARRAY(source, n);
-			REALLOC_ARRAY(destination, n);
 			REALLOC_ARRAY(modes, n);
-			REALLOC_ARRAY(submodule_gitfile, n);
+			REALLOC_ARRAY(submodule_gitfiles, n);
 
-			dst = add_slash(dst);
-			dst_len = strlen(dst);
+			dst_with_slash = add_slash(dst);
+			dst_with_slash_len = strlen(dst_with_slash);
 
 			for (j = 0; j < last - first; j++) {
-				const struct cache_entry *ce = the_index.cache[first + j];
+				const struct cache_entry *ce = the_repository->index->cache[first + j];
 				const char *path = ce->name;
-				source[argc + j] = path;
-				destination[argc + j] =
-					prefix_path(dst, dst_len, path + length + 1);
+				char *prefixed_path = prefix_path(dst_with_slash, dst_with_slash_len, path + length + 1);
+
+				strvec_push(&sources, path);
+				strvec_push(&destinations, prefixed_path);
+
 				memset(modes + argc + j, 0, sizeof(enum update_mode));
 				modes[argc + j] |= ce_skip_worktree(ce) ? SPARSE : INDEX;
-				submodule_gitfile[argc + j] = NULL;
+				submodule_gitfiles[argc + j] = NULL;
+
+				free(prefixed_path);
 			}
+
+			free(dst_with_slash);
 			argc += last - first;
 			goto act_on_entry;
 		}
-		if (!(ce = index_file_exists(&the_index, src, length, 0))) {
+		if (!(ce = index_file_exists(the_repository->index, src, length, 0))) {
 			bad = _("not under version control");
 			goto act_on_entry;
 		}
@@ -387,7 +407,7 @@
 
 		if (ignore_sparse &&
 		    (dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
-		    index_entry_exists(&the_index, dst, strlen(dst))) {
+		    index_entry_exists(the_repository->index, dst, strlen(dst))) {
 			bad = _("destination exists in the index");
 			if (force) {
 				if (verbose)
@@ -404,12 +424,12 @@
 		 * option as a way to have a successful run.
 		 */
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(src, &the_index)) {
+		    !path_in_sparse_checkout(src, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, src);
 			skip_sparse = 1;
 		}
 		if (!ignore_sparse &&
-		    !path_in_sparse_checkout(dst, &the_index)) {
+		    !path_in_sparse_checkout(dst, the_repository->index)) {
 			string_list_append(&only_match_skip_worktree, dst);
 			skip_sparse = 1;
 		}
@@ -428,28 +448,30 @@
 remove_entry:
 		if (--argc > 0) {
 			int n = argc - i;
-			MOVE_ARRAY(source + i, source + i + 1, n);
-			MOVE_ARRAY(destination + i, destination + i + 1, n);
+			strvec_remove(&sources, i);
+			strvec_remove(&destinations, i);
 			MOVE_ARRAY(modes + i, modes + i + 1, n);
-			MOVE_ARRAY(submodule_gitfile + i,
-				   submodule_gitfile + i + 1, n);
+			MOVE_ARRAY(submodule_gitfiles + i,
+				   submodule_gitfiles + i + 1, n);
 			i--;
 		}
 	}
 
 	if (only_match_skip_worktree.nr) {
 		advise_on_updating_sparse_paths(&only_match_skip_worktree);
-		if (!ignore_errors)
-			return 1;
+		if (!ignore_errors) {
+			ret = 1;
+			goto out;
+		}
 	}
 
 	for (i = 0; i < argc; i++) {
-		const char *src = source[i], *dst = destination[i];
+		const char *src = sources.v[i], *dst = destinations.v[i];
 		enum update_mode mode = modes[i];
 		int pos;
 		int sparse_and_dirty = 0;
 		struct checkout state = CHECKOUT_INIT;
-		state.istate = &the_index;
+		state.istate = the_repository->index;
 
 		if (force)
 			state.force = 1;
@@ -464,26 +486,26 @@
 				continue;
 			die_errno(_("renaming '%s' failed"), src);
 		}
-		if (submodule_gitfile[i]) {
+		if (submodule_gitfiles[i]) {
 			if (!update_path_in_gitmodules(src, dst))
 				gitmodules_modified = 1;
-			if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+			if (submodule_gitfiles[i] != SUBMODULE_WITH_GITDIR)
 				connect_work_tree_and_git_dir(dst,
-							      submodule_gitfile[i],
+							      submodule_gitfiles[i],
 							      1);
 		}
 
 		if (mode & (WORKING_DIRECTORY | SKIP_WORKTREE_DIR))
 			continue;
 
-		pos = index_name_pos(&the_index, src, strlen(src));
+		pos = index_name_pos(the_repository->index, src, strlen(src));
 		assert(pos >= 0);
 		if (!(mode & SPARSE) && !lstat(src, &st))
-			sparse_and_dirty = ie_modified(&the_index,
-						       the_index.cache[pos],
+			sparse_and_dirty = ie_modified(the_repository->index,
+						       the_repository->index->cache[pos],
 						       &st,
 						       0);
-		rename_index_entry_at(&the_index, pos, dst);
+		rename_index_entry_at(the_repository->index, pos, dst);
 
 		if (ignore_sparse &&
 		    core_apply_sparse_checkout &&
@@ -495,11 +517,11 @@
 			 * should be added in a future patch.
 			 */
 			if ((mode & SPARSE) &&
-			    path_in_sparse_checkout(dst, &the_index)) {
+			    path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from out-of-cone to in-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				dst_ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
@@ -507,11 +529,11 @@
 					die(_("cannot checkout %s"), dst_ce->name);
 			} else if ((dst_mode & (SKIP_WORKTREE_DIR | SPARSE)) &&
 				   !(mode & SPARSE) &&
-				   !path_in_sparse_checkout(dst, &the_index)) {
+				   !path_in_sparse_checkout(dst, the_repository->index)) {
 				/* from in-cone to out-of-cone */
-				int dst_pos = index_name_pos(&the_index, dst,
+				int dst_pos = index_name_pos(the_repository->index, dst,
 							     strlen(dst));
-				struct cache_entry *dst_ce = the_index.cache[dst_pos];
+				struct cache_entry *dst_ce = the_repository->index->cache[dst_pos];
 
 				/*
 				 * if src is clean, it will suffice to remove it
@@ -535,41 +557,31 @@
 		}
 	}
 
-	/*
-	 * cleanup the empty src_dirs
-	 */
-	for (i = 0; i < src_dir_nr; i++) {
-		int dummy;
-		strbuf_addstr(&a_src_dir, src_dir[i]);
-		/*
-		 * if entries under a_src_dir are all moved away,
-		 * recursively remove a_src_dir to cleanup
-		 */
-		if (index_range_of_same_dir(a_src_dir.buf, a_src_dir.len,
-					    &dummy, &dummy) < 1) {
-			remove_dir_recursively(&a_src_dir, 0);
-		}
-		strbuf_reset(&a_src_dir);
-	}
-
-	strbuf_release(&a_src_dir);
-	free(src_dir);
+	remove_empty_src_dirs(src_dir.v, src_dir.nr);
 
 	if (dirty_paths.nr)
 		advise_on_moving_dirty_path(&dirty_paths);
 
 	if (gitmodules_modified)
-		stage_updated_gitmodules(&the_index);
+		stage_updated_gitmodules(the_repository->index);
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
+	ret = 0;
+
+out:
+	strvec_clear(&src_dir);
+	free(dst_w_slash);
 	string_list_clear(&src_for_dst, 0);
 	string_list_clear(&dirty_paths, 0);
-	UNLEAK(source);
-	UNLEAK(dest_path);
-	free(submodule_gitfile);
+	string_list_clear(&only_match_skip_worktree, 0);
+	strvec_clear(&sources);
+	strvec_clear(&dest_paths);
+	strvec_clear(&destinations);
+	strvec_clear(&submodule_gitfiles_to_free);
+	free(submodule_gitfiles);
 	free(modes);
-	return 0;
+	return ret;
 }
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index ad9930c..70e9ec4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -296,7 +296,8 @@
 	char *short_refname = NULL;
 
 	if (shorten_unambiguous)
-		short_refname = shorten_unambiguous_ref(refname, 0);
+		short_refname = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+							     refname, 0);
 	else if (skip_prefix(refname, "refs/heads/", &refname))
 		; /* refname already advanced */
 	else
@@ -647,7 +648,7 @@
 
 	adjust_cutoff_timestamp_for_slop();
 
-	for_each_ref(name_ref, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository), name_ref, &data);
 	name_tips(&string_pool);
 
 	if (annotate_stdin) {
diff --git a/builtin/notes.c b/builtin/notes.c
index cb01130..7f80b34 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -794,9 +794,9 @@
 	 * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
 	 */
 
-	if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
 		ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
-	if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF))
 		ret += error(_("failed to delete ref NOTES_MERGE_REF"));
 	if (notes_merge_abort(o))
 		ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -834,7 +834,8 @@
 	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
 	o->local_ref = local_ref_to_free =
-		resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL);
+		refs_resolve_refdup(get_main_ref_store(the_repository),
+				    "NOTES_MERGE_REF", 0, &oid, NULL);
 	if (!o->local_ref)
 		die(_("failed to resolve NOTES_MERGE_REF"));
 
@@ -847,9 +848,10 @@
 				   &pretty_ctx);
 	strbuf_trim(&msg);
 	strbuf_insertstr(&msg, 0, "notes: ");
-	update_ref(msg.buf, o->local_ref, &oid,
-		   is_null_oid(&parent_oid) ? NULL : &parent_oid,
-		   0, UPDATE_REFS_DIE_ON_ERR);
+	refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+			o->local_ref, &oid,
+			is_null_oid(&parent_oid) ? NULL : &parent_oid,
+			0, UPDATE_REFS_DIE_ON_ERR);
 
 	free_notes(t);
 	strbuf_release(&msg);
@@ -961,14 +963,16 @@
 
 	if (result >= 0) /* Merge resulted (trivially) in result_oid */
 		/* Update default notes ref with new commit */
-		update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
-			   UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				default_notes_ref(), &result_oid, NULL, 0,
+				UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
 		struct worktree **worktrees;
 		const struct worktree *wt;
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
-		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
-			   0, UPDATE_REFS_DIE_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"NOTES_MERGE_PARTIAL", &result_oid, NULL,
+				0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
 		worktrees = get_worktrees();
 		wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
@@ -977,7 +981,7 @@
 			die(_("a notes merge into %s is already in-progress at %s"),
 			    default_notes_ref(), wt->path);
 		free_worktrees(worktrees);
-		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
+		if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
 			die(_("failed to store link to current notes ref (%s)"),
 			    default_notes_ref());
 		fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index baf0090..638f5c5 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -779,7 +779,7 @@
 
 	if (entry)
 		entry->tagged = 1;
-	if (!peel_iterated_oid(oid, &peeled)) {
+	if (!peel_iterated_oid(the_repository, oid, &peeled)) {
 		entry = packlist_find(&to_pack, &peeled);
 		if (entry)
 			entry->tagged = 1;
@@ -939,7 +939,8 @@
 	/*
 	 * Mark objects that are at the tip of tags.
 	 */
-	for_each_tag_ref(mark_tagged, NULL);
+	refs_for_each_tag_ref(get_main_ref_store(the_repository), mark_tagged,
+			      NULL);
 
 	if (use_delta_islands) {
 		max_layers = compute_pack_layers(&to_pack);
@@ -1314,6 +1315,7 @@
 		if (!pack_to_stdout) {
 			struct stat st;
 			struct strbuf tmpname = STRBUF_INIT;
+			struct bitmap_writer bitmap_writer;
 			char *idx_tmp_name = NULL;
 
 			/*
@@ -1339,8 +1341,9 @@
 				    hash_to_hex(hash));
 
 			if (write_bitmap_index) {
-				bitmap_writer_set_checksum(hash);
-				bitmap_writer_build_type_index(
+				bitmap_writer_init(&bitmap_writer);
+				bitmap_writer_set_checksum(&bitmap_writer, hash);
+				bitmap_writer_build_type_index(&bitmap_writer,
 					&to_pack, written_list, nr_written);
 			}
 
@@ -1358,12 +1361,17 @@
 				strbuf_addstr(&tmpname, "bitmap");
 				stop_progress(&progress_state);
 
-				bitmap_writer_show_progress(progress);
-				bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
-				if (bitmap_writer_build(&to_pack) < 0)
+				bitmap_writer_show_progress(&bitmap_writer,
+							    progress);
+				bitmap_writer_select_commits(&bitmap_writer,
+							     indexed_commits,
+							     indexed_commits_nr);
+				if (bitmap_writer_build(&bitmap_writer, &to_pack) < 0)
 					die(_("failed to write bitmap index"));
-				bitmap_writer_finish(written_list, nr_written,
+				bitmap_writer_finish(&bitmap_writer,
+						     written_list, nr_written,
 						     tmpname.buf, write_bitmap_options);
+				bitmap_writer_free(&bitmap_writer);
 				write_bitmap_index = 0;
 				strbuf_setlen(&tmpname, tmpname_len);
 			}
@@ -3124,7 +3132,7 @@
 {
 	struct object_id peeled;
 
-	if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled))
+	if (!peel_iterated_oid(the_repository, oid, &peeled) && obj_is_packed(&peeled))
 		add_tag_chain(oid);
 	return 0;
 }
@@ -4073,7 +4081,7 @@
 	struct object_id peeled;
 	struct object *object;
 
-	if (!peel_iterated_oid(oid, &peeled))
+	if (!peel_iterated_oid(the_repository, oid, &peeled))
 		oid = &peeled;
 
 	object = parse_object_or_die(oid, refname);
@@ -4093,7 +4101,9 @@
 		return;
 
 	for_each_string_list_item(item, preferred_tips) {
-		for_each_ref_in(item->string, mark_bitmap_preferred_tip, NULL);
+		refs_for_each_ref_in(get_main_ref_store(the_repository),
+				     item->string, mark_bitmap_preferred_tip,
+				     NULL);
 	}
 }
 
@@ -4588,7 +4598,8 @@
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
-		for_each_tag_ref(add_ref_tag, NULL);
+		refs_for_each_tag_ref(get_main_ref_store(the_repository),
+				      add_ref_tag, NULL);
 	stop_progress(&progress_state);
 	trace2_region_leave("pack-objects", "enumerate-objects",
 			    the_repository);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 3894d2b..583099c 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -5,6 +5,7 @@
 #include "hash.h"
 #include "hex.h"
 #include "parse-options.h"
+#include "setup.h"
 
 static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
 {
@@ -237,6 +238,18 @@
 	argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
 			     patch_id_usage, 0);
 
+	/*
+	 * We rely on `the_hash_algo` to compute patch IDs. This is dubious as
+	 * it means that the hash algorithm now depends on the object hash of
+	 * the repository, even though git-patch-id(1) clearly defines that
+	 * patch IDs always use SHA1.
+	 *
+	 * NEEDSWORK: This hack should be removed in favor of converting
+	 * the code that computes patch IDs to always use SHA1.
+	 */
+	if (!the_hash_algo)
+		repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
+
 	generate_id_list(opts ? opts > 1 : config.stable,
 			 opts ? opts == 3 : config.verbatim);
 	return 0;
diff --git a/builtin/pull.c b/builtin/pull.c
index 72cbb76..d622202 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -5,7 +5,7 @@
  *
  * Fetch one or more remote refs and merge it/them into the current HEAD.
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -611,7 +611,7 @@
 				  merge_head, 0))
 		return 1;
 
-	if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+	if (refs_update_ref(get_main_ref_store(the_repository), "initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
 		return 1;
 
 	return 0;
@@ -1044,7 +1044,7 @@
 		if (opt_autostash == -1)
 			opt_autostash = config_autostash;
 
-		if (is_null_oid(&orig_head) && !is_index_unborn(&the_index))
+		if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index))
 			die(_("Updating an unborn branch with changes added to the index."));
 
 		if (!opt_autostash)
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 6f89cec..a8cf850 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -4,7 +4,6 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
 #include "gettext.h"
@@ -159,8 +158,8 @@
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = -1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 
 	git_config(git_read_tree_config, NULL);
 
@@ -197,7 +196,7 @@
 			die(_("You need to resolve your current index first"));
 		stage = opts.merge = 1;
 	}
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 
 	for (i = 0; i < argc; i++) {
 		const char *arg = argv[i];
@@ -225,7 +224,7 @@
 		setup_work_tree();
 
 	if (opts.skip_sparse_checkout)
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 	if (opts.merge) {
 		switch (stage - 1) {
@@ -237,7 +236,7 @@
 			break;
 		case 2:
 			opts.fn = twoway_merge;
-			opts.initial_checkout = is_index_unborn(&the_index);
+			opts.initial_checkout = is_index_unborn(the_repository->index);
 			break;
 		case 3:
 		default:
@@ -258,7 +257,7 @@
 	if (nr_trees == 1 && !opts.prefix)
 		opts.skip_cache_tree_update = 1;
 
-	cache_tree_free(&the_index.cache_tree);
+	cache_tree_free(&the_repository->index->cache_tree);
 	for (i = 0; i < nr_trees; i++) {
 		struct tree *tree = trees[i];
 		if (parse_tree(tree) < 0)
@@ -282,7 +281,7 @@
 				 the_repository->index,
 				 trees[0]);
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		die("unable to write new index file");
 	return 0;
 }
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 891f284..14d4f0a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,7 +4,6 @@
  * Copyright (c) 2018 Pratik Karki
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -84,7 +83,7 @@
 struct rebase_options {
 	enum rebase_type type;
 	enum empty_type empty;
-	const char *default_backend;
+	char *default_backend;
 	const char *state_dir;
 	struct commit *upstream;
 	const char *upstream_name;
@@ -252,7 +251,7 @@
 	if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
 		return error_errno(_("could not create temporary %s"), merge_dir());
 
-	delete_reflog("REBASE_HEAD");
+	refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD");
 
 	interactive = fopen(path_interactive(), "w");
 	if (!interactive)
@@ -295,7 +294,7 @@
 	if (ret)
 		error(_("could not generate todo list"));
 	else {
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
 						&todo_list))
 			BUG("unusable todo list");
@@ -514,8 +513,10 @@
 	struct strbuf dir = STRBUF_INIT;
 	int ret = 0;
 
-	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"REBASE_HEAD", NULL, REF_NO_DEREF);
+	refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			"AUTO_MERGE", NULL, REF_NO_DEREF);
 	apply_autostash(state_dir_path("autostash", opts));
 	/*
 	 * We ignore errors in 'git maintenance run --auto', since the
@@ -1623,7 +1624,7 @@
 		/* Is it a local branch? */
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "refs/heads/%s", branch_name);
-		if (!read_ref(buf.buf, &branch_oid)) {
+		if (!refs_read_ref(get_main_ref_store(the_repository), buf.buf, &branch_oid)) {
 			die_if_checked_out(buf.buf, 1);
 			options.head_name = xstrdup(buf.buf);
 			options.orig_head =
@@ -1640,8 +1641,8 @@
 	} else if (argc == 0) {
 		/* Do not need to switch branches, we are already on it. */
 		options.head_name =
-			xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
-					 &flags));
+			xstrdup_or_null(refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL,
+								&flags));
 		if (!options.head_name)
 			die(_("No such ref: %s"), "HEAD");
 		if (flags & REF_ISSYMREF) {
@@ -1735,7 +1736,7 @@
 			if (!(options.flags & REBASE_NO_QUIET))
 				; /* be quiet */
 			else if (!strcmp(branch_name, "HEAD") &&
-				 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+				 refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
 				puts(_("HEAD is up to date."));
 			else
 				printf(_("Current branch %s is up to date.\n"),
@@ -1745,7 +1746,7 @@
 		} else if (!(options.flags & REBASE_NO_QUIET))
 			; /* be quiet */
 		else if (!strcmp(branch_name, "HEAD") &&
-			 resolve_ref_unsafe("HEAD", 0, NULL, &flag))
+			 refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flag))
 			puts(_("HEAD is up to date, rebase forced."));
 		else
 			printf(_("Current branch %s is up to date, rebase "
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index e8d7df1..01c1f04 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -88,7 +88,7 @@
 static struct object_id push_cert_oid;
 static struct signature_check sigcheck;
 static const char *push_cert_nonce;
-static const char *cert_nonce_seed;
+static char *cert_nonce_seed;
 static struct strvec hidden_refs = STRVEC_INIT;
 
 static const char *NONCE_UNSOLICITED = "UNSOLICITED";
@@ -168,13 +168,13 @@
 	}
 
 	if (strcmp(var, "receive.fsck.skiplist") == 0) {
-		const char *path;
+		char *path;
 
 		if (git_config_pathname(&path, var, value))
 			return 1;
 		strbuf_addf(&fsck_msg_types, "%cskiplist=%s",
 			fsck_msg_types.len ? ',' : '=', path);
-		free((char *)path);
+		free(path);
 		return 0;
 	}
 
@@ -1566,7 +1566,7 @@
 		struct strbuf err = STRBUF_INIT;
 		if (!parse_object(the_repository, old_oid)) {
 			old_oid = NULL;
-			if (ref_exists(name)) {
+			if (refs_ref_exists(get_main_ref_store(the_repository), name)) {
 				rp_warning("allowing deletion of corrupt ref");
 			} else {
 				rp_warning("deleting a non-existent ref");
@@ -1595,6 +1595,7 @@
 		if (ref_transaction_update(transaction,
 					   namespaced_name,
 					   new_oid, old_oid,
+					   NULL, NULL,
 					   0, "push",
 					   &err)) {
 			rp_error("%s", err.buf);
@@ -1693,7 +1694,8 @@
 	int flag;
 
 	strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
-	dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
+	dst_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					   buf.buf, 0, NULL, &flag);
 	check_aliased_update_internal(cmd, list, dst_name, flag);
 	strbuf_release(&buf);
 }
@@ -1829,7 +1831,8 @@
 		if (!should_process_cmd(cmd) || cmd->run_proc_receive)
 			continue;
 
-		transaction = ref_transaction_begin(&err);
+		transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+							  &err);
 		if (!transaction) {
 			rp_error("%s", err.buf);
 			strbuf_reset(&err);
@@ -1857,7 +1860,8 @@
 	struct strbuf err = STRBUF_INIT;
 	const char *reported_error = "atomic push failure";
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction) {
 		rp_error("%s", err.buf);
 		strbuf_reset(&err);
@@ -1983,7 +1987,9 @@
 	check_aliased_updates(commands);
 
 	free(head_name_to_free);
-	head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
+	head_name = head_name_to_free = refs_resolve_refdup(get_main_ref_store(the_repository),
+							    "HEAD", 0, NULL,
+							    NULL);
 
 	if (run_proc_receive &&
 	    run_proc_receive_hook(commands, push_options))
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 060eb33..0d2ff95 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -364,11 +364,12 @@
 			};
 
 			set_reflog_expiry_param(&cb.cmd,  item->string);
-			status |= reflog_expire(item->string, flags,
-						reflog_expiry_prepare,
-						should_prune_fn,
-						reflog_expiry_cleanup,
-						&cb);
+			status |= refs_reflog_expire(get_main_ref_store(the_repository),
+						     item->string, flags,
+						     reflog_expiry_prepare,
+						     should_prune_fn,
+						     reflog_expiry_cleanup,
+						     &cb);
 		}
 		string_list_clear(&collected.reflogs, 0);
 	}
@@ -377,16 +378,17 @@
 		char *ref;
 		struct expire_reflog_policy_cb cb = { .cmd = cmd };
 
-		if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
+		if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
 			status |= error(_("%s points nowhere!"), argv[i]);
 			continue;
 		}
 		set_reflog_expiry_param(&cb.cmd, ref);
-		status |= reflog_expire(ref, flags,
-					reflog_expiry_prepare,
-					should_prune_fn,
-					reflog_expiry_cleanup,
-					&cb);
+		status |= refs_reflog_expire(get_main_ref_store(the_repository),
+					     ref, flags,
+					     reflog_expiry_prepare,
+					     should_prune_fn,
+					     reflog_expiry_cleanup,
+					     &cb);
 		free(ref);
 	}
 	return status;
@@ -437,7 +439,8 @@
 	refname = argv[0];
 	if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
 		die(_("invalid ref format: %s"), refname);
-	return !reflog_exists(refname);
+	return !refs_reflog_exists(get_main_ref_store(the_repository),
+				   refname);
 }
 
 /*
diff --git a/builtin/refs.c b/builtin/refs.c
new file mode 100644
index 0000000..46dcd15
--- /dev/null
+++ b/builtin/refs.c
@@ -0,0 +1,75 @@
+#include "builtin.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "repository.h"
+#include "strbuf.h"
+
+#define REFS_MIGRATE_USAGE \
+	N_("git refs migrate --ref-format=<format> [--dry-run]")
+
+static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
+{
+	const char * const migrate_usage[] = {
+		REFS_MIGRATE_USAGE,
+		NULL,
+	};
+	const char *format_str = NULL;
+	enum ref_storage_format format;
+	unsigned int flags = 0;
+	struct option options[] = {
+		OPT_STRING_F(0, "ref-format", &format_str, N_("format"),
+			N_("specify the reference format to convert to"),
+			PARSE_OPT_NONEG),
+		OPT_BIT(0, "dry-run", &flags,
+			N_("perform a non-destructive dry-run"),
+			REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
+		OPT_END(),
+	};
+	struct strbuf errbuf = STRBUF_INIT;
+	int err;
+
+	argc = parse_options(argc, argv, prefix, options, migrate_usage, 0);
+	if (argc)
+		usage(_("too many arguments"));
+	if (!format_str)
+		usage(_("missing --ref-format=<format>"));
+
+	format = ref_storage_format_by_name(format_str);
+	if (format == REF_STORAGE_FORMAT_UNKNOWN) {
+		err = error(_("unknown ref storage format '%s'"), format_str);
+		goto out;
+	}
+
+	if (the_repository->ref_storage_format == format) {
+		err = error(_("repository already uses '%s' format"),
+			    ref_storage_format_to_name(format));
+		goto out;
+	}
+
+	if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) {
+		err = error("%s", errbuf.buf);
+		goto out;
+	}
+
+	err = 0;
+
+out:
+	strbuf_release(&errbuf);
+	return err;
+}
+
+int cmd_refs(int argc, const char **argv, const char *prefix)
+{
+	const char * const refs_usage[] = {
+		REFS_MIGRATE_USAGE,
+		NULL,
+	};
+	parse_opt_subcommand_fn *fn = NULL;
+	struct option opts[] = {
+		OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
+		OPT_END(),
+	};
+
+	argc = parse_options(argc, argv, prefix, opts, refs_usage, 0);
+	return fn(argc, argv, prefix);
+}
diff --git a/builtin/remote.c b/builtin/remote.c
index 8412d12..447ef1d 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -240,7 +240,7 @@
 		strbuf_reset(&buf2);
 		strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
 
-		if (create_symref(buf.buf, buf2.buf, "remote add"))
+		if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
 			return error(_("Could not setup master '%s'"), master);
 	}
 
@@ -376,7 +376,7 @@
 	for (ref = fetch_map; ref; ref = ref->next) {
 		if (omit_name_by_refspec(ref->name, &states->remote->fetch))
 			string_list_append(&states->skipped, abbrev_branch(ref->name));
-		else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+		else if (!ref->peer_ref || !refs_ref_exists(get_main_ref_store(the_repository), ref->peer_ref->name))
 			string_list_append(&states->new_refs, abbrev_branch(ref->name));
 		else
 			string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -598,8 +598,9 @@
 	strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
 	if (starts_with(refname, buf.buf)) {
 		item = string_list_append(rename->remote_branches, refname);
-		symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
-					    NULL, &flag);
+		symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						 refname, RESOLVE_REF_READING,
+						 NULL, &flag);
 		if (symref && (flag & REF_ISSYMREF)) {
 			item->util = xstrdup(symref);
 			rename->symrefs_nr++;
@@ -789,7 +790,8 @@
 	 * First remove symrefs, then rename the rest, finally create
 	 * the new symrefs.
 	 */
-	for_each_ref(read_remote_branches, &rename);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  read_remote_branches, &rename);
 	if (show_progress) {
 		/*
 		 * Count symrefs twice, since "renaming" them is done by
@@ -805,7 +807,7 @@
 		if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
 					   &referent))
 			continue;
-		if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
+		if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF))
 			die(_("deleting '%s' failed"), item->string);
 
 		strbuf_release(&referent);
@@ -823,7 +825,7 @@
 		strbuf_reset(&buf2);
 		strbuf_addf(&buf2, "remote: renamed %s to %s",
 				item->string, buf.buf);
-		if (rename_ref(item->string, buf.buf, buf2.buf))
+		if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf))
 			die(_("renaming '%s' failed"), item->string);
 		display_progress(progress, ++refs_renamed_nr);
 	}
@@ -843,7 +845,7 @@
 		strbuf_reset(&buf3);
 		strbuf_addf(&buf3, "remote: renamed %s to %s",
 				item->string, buf.buf);
-		if (create_symref(buf.buf, buf2.buf, buf3.buf))
+		if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
 			die(_("creating '%s' failed"), buf.buf);
 		display_progress(progress, ++refs_renamed_nr);
 	}
@@ -917,11 +919,14 @@
 	 * refs, which are invalidated when deleting a branch.
 	 */
 	cb_data.remote = remote;
-	result = for_each_ref(add_branch_for_removal, &cb_data);
+	result = refs_for_each_ref(get_main_ref_store(the_repository),
+				   add_branch_for_removal, &cb_data);
 	strbuf_release(&buf);
 
 	if (!result)
-		result = delete_refs("remote: remove", &branches, REF_NO_DEREF);
+		result = refs_delete_refs(get_main_ref_store(the_repository),
+					  "remote: remove", &branches,
+					  REF_NO_DEREF);
 	string_list_clear(&branches, 0);
 
 	if (skipped.nr) {
@@ -1010,7 +1015,8 @@
 			get_push_ref_states(remote_refs, states);
 		transport_disconnect(transport);
 	} else {
-		for_each_ref(append_ref_to_tracked_list, states);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_ref_to_tracked_list, states);
 		string_list_sort(&states->tracked);
 		get_push_ref_states_noquery(states);
 	}
@@ -1407,7 +1413,7 @@
 			head_name = xstrdup(states.heads.items[0].string);
 		free_remote_ref_states(&states);
 	} else if (opt_d && !opt_a && argc == 1) {
-		if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF))
+		if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
 			result |= error(_("Could not delete %s"), buf.buf);
 	} else
 		usage_with_options(builtin_remote_sethead_usage, options);
@@ -1415,9 +1421,9 @@
 	if (head_name) {
 		strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
 		/* make sure it's valid */
-		if (!ref_exists(buf2.buf))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
 			result |= error(_("Not a valid ref: %s"), buf2.buf);
-		else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
+		else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
 			result |= error(_("Could not setup %s"), buf.buf);
 		else if (opt_a)
 			printf("%s/HEAD set to %s\n", argv[0], head_name);
@@ -1457,7 +1463,8 @@
 	string_list_sort(&refs_to_prune);
 
 	if (!dry_run)
-		result |= delete_refs("remote: prune", &refs_to_prune, 0);
+		result |= refs_delete_refs(get_main_ref_store(the_repository),
+					   "remote: prune", &refs_to_prune, 0);
 
 	for_each_string_list_item(item, &states.stale) {
 		const char *refname = item->util;
@@ -1470,7 +1477,8 @@
 			       abbrev_ref(refname, "refs/remotes/"));
 	}
 
-	warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune);
+	refs_warn_dangling_symrefs(get_main_ref_store(the_repository),
+				   stdout, dangling_msg, &refs_to_prune);
 
 	string_list_clear(&refs_to_prune, 0);
 	free_remote_ref_states(&states);
diff --git a/builtin/repack.c b/builtin/repack.c
index 15e4ccc..f0317fa 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -48,10 +48,10 @@
 );
 
 struct pack_objects_args {
-	const char *window;
-	const char *window_memory;
-	const char *depth;
-	const char *threads;
+	char *window;
+	char *window_memory;
+	char *depth;
+	char *threads;
 	unsigned long max_pack_size;
 	int no_reuse_delta;
 	int no_reuse_object;
@@ -673,7 +673,7 @@
 	struct midx_snapshot_ref_data *data = _data;
 	struct object_id peeled;
 
-	if (!peel_iterated_oid(oid, &peeled))
+	if (!peel_iterated_oid(the_repository, oid, &peeled))
 		oid = &peeled;
 
 	if (oidset_insert(&data->seen, oid))
@@ -706,11 +706,14 @@
 
 		data.preferred = 1;
 		for_each_string_list_item(item, preferred)
-			for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+			refs_for_each_ref_in(get_main_ref_store(the_repository),
+					     item->string,
+					     midx_snapshot_ref_one, &data);
 		data.preferred = 0;
 	}
 
-	for_each_ref(midx_snapshot_ref_one, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository),
+			  midx_snapshot_ref_one, &data);
 
 	if (close_tempfile_gently(f)) {
 		int save_errno = errno;
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600..ce9f697 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -43,11 +43,12 @@
 };
 
 struct show_data {
+	struct repository *repo;
 	const char *pattern;
 	enum replace_format format;
 };
 
-static int show_reference(struct repository *r, const char *refname,
+static int show_reference(const char *refname,
 			  const struct object_id *oid,
 			  int flag UNUSED, void *cb_data)
 {
@@ -62,11 +63,11 @@
 			struct object_id object;
 			enum object_type obj_type, repl_type;
 
-			if (repo_get_oid(r, refname, &object))
+			if (repo_get_oid(data->repo, refname, &object))
 				return error(_("failed to resolve '%s' as a valid ref"), refname);
 
-			obj_type = oid_object_info(r, &object, NULL);
-			repl_type = oid_object_info(r, oid, NULL);
+			obj_type = oid_object_info(data->repo, &object, NULL);
+			repl_type = oid_object_info(data->repo, oid, NULL);
 
 			printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
 			       oid_to_hex(oid), type_name(repl_type));
@@ -80,6 +81,7 @@
 {
 	struct show_data data;
 
+	data.repo = the_repository;
 	if (!pattern)
 		pattern = "*";
 	data.pattern = pattern;
@@ -99,7 +101,8 @@
 			       "valid formats are 'short', 'medium' and 'long'"),
 			     format);
 
-	for_each_replace_ref(the_repository, show_reference, (void *)&data);
+	refs_for_each_replace_ref(get_main_ref_store(the_repository),
+				  show_reference, (void *)&data);
 
 	return 0;
 }
@@ -130,7 +133,7 @@
 		strbuf_addstr(&ref, oid_to_hex(&oid));
 		full_hex = ref.buf + base_len;
 
-		if (read_ref(ref.buf, &oid)) {
+		if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
 			error(_("replace ref '%s' not found"), full_hex);
 			had_error = 1;
 			continue;
@@ -145,7 +148,7 @@
 static int delete_replace_ref(const char *name, const char *ref,
 			      const struct object_id *oid)
 {
-	if (delete_ref(NULL, ref, oid, 0))
+	if (refs_delete_ref(get_main_ref_store(the_repository), NULL, ref, oid, 0))
 		return 1;
 	printf_ln(_("Deleted replace ref '%s'"), name);
 	return 0;
@@ -163,7 +166,7 @@
 	if (check_refname_format(ref->buf, 0))
 		return error(_("'%s' is not a valid ref name"), ref->buf);
 
-	if (read_ref(ref->buf, prev))
+	if (refs_read_ref(get_main_ref_store(the_repository), ref->buf, prev))
 		oidclr(prev);
 	else if (!force)
 		return error(_("replace ref '%s' already exists"), ref->buf);
@@ -198,10 +201,11 @@
 		return -1;
 	}
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, repl, &prev,
-				   0, NULL, &err) ||
+				   NULL, NULL, 0, NULL, &err) ||
 	    ref_transaction_commit(transaction, &err))
 		res = error("%s", err.buf);
 
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bc4b47..6bf0691 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -2,7 +2,6 @@
  * "git replay" builtin command
  */
 
-#define USE_THE_INDEX_VARIABLE
 #include "git-compat-util.h"
 
 #include "builtin.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index 1d62ff6..5f941fb 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -66,8 +66,8 @@
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.fn = oneway_merge;
 	opts.merge = 1;
 	init_checkout_metadata(&opts.meta, ref, oid, NULL);
@@ -159,11 +159,11 @@
 		struct cache_entry *ce;
 
 		if (!is_in_reset_tree && !intent_to_add) {
-			remove_file_from_index(&the_index, one->path);
+			remove_file_from_index(the_repository->index, one->path);
 			continue;
 		}
 
-		ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
+		ce = make_cache_entry(the_repository->index, one->mode, &one->oid, one->path,
 				      0, 0);
 
 		/*
@@ -174,9 +174,9 @@
 		 * if this entry is outside the sparse cone - this is necessary
 		 * to properly construct the reset sparse directory.
 		 */
-		pos = index_name_pos(&the_index, one->path, strlen(one->path));
-		if ((pos >= 0 && ce_skip_worktree(the_index.cache[pos])) ||
-		    (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+		pos = index_name_pos(the_repository->index, one->path, strlen(one->path));
+		if ((pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) ||
+		    (pos < 0 && !path_in_sparse_checkout(one->path, the_repository->index)))
 			ce->ce_flags |= CE_SKIP_WORKTREE;
 
 		if (!ce)
@@ -186,7 +186,7 @@
 			ce->ce_flags |= CE_INTENT_TO_ADD;
 			set_object_name_for_intent_to_add_entry(ce);
 		}
-		add_index_entry(&the_index, ce,
+		add_index_entry(the_repository->index, ce,
 				ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 	}
 }
@@ -208,8 +208,8 @@
 	opt.change = diff_change;
 	opt.add_remove = diff_addremove;
 
-	if (pathspec->nr && pathspec_needs_expanded_index(&the_index, pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec->nr && pathspec_needs_expanded_index(the_repository->index, pathspec))
+		ensure_full_index(the_repository->index);
 
 	if (do_diff_cache(tree_oid, &opt))
 		return 1;
@@ -235,7 +235,7 @@
 
 static void die_if_unmerged_cache(int reset_type)
 {
-	if (is_merge() || unmerged_index(&the_index))
+	if (is_merge() || unmerged_index(the_repository->index))
 		die(_("Cannot do a %s reset in the middle of a merge."),
 		    _(reset_type_names[reset_type]));
 
@@ -307,13 +307,16 @@
 	if (!repo_get_oid(the_repository, "HEAD", &oid_orig)) {
 		orig = &oid_orig;
 		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
-			   UPDATE_REFS_MSG_ON_ERR);
+		refs_update_ref(get_main_ref_store(the_repository), msg.buf,
+				"ORIG_HEAD", orig, old_orig, 0,
+				UPDATE_REFS_MSG_ON_ERR);
 	} else if (old_orig)
-		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+		refs_delete_ref(get_main_ref_store(the_repository), NULL,
+				"ORIG_HEAD", old_orig, 0);
 	set_reflog_message(&msg, "updating HEAD", rev);
-	update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0,
-				       UPDATE_REFS_MSG_ON_ERR);
+	update_ref_status = refs_update_ref(get_main_ref_store(the_repository),
+					    msg.buf, "HEAD", oid, orig, 0,
+					    UPDATE_REFS_MSG_ON_ERR);
 	strbuf_release(&msg);
 	return update_ref_status;
 }
@@ -470,12 +473,12 @@
 				update_ref_status = 1;
 				goto cleanup;
 			}
-			the_index.updated_skipworktree = 1;
+			the_repository->index->updated_skipworktree = 1;
 			if (!no_refresh && get_git_work_tree()) {
 				uint64_t t_begin, t_delta_in_ms;
 
 				t_begin = getnanotime();
-				refresh_index(&the_index, flags, NULL, NULL,
+				refresh_index(the_repository->index, flags, NULL, NULL,
 					      _("Unstaged changes after reset:"));
 				t_delta_in_ms = (getnanotime() - t_begin) / 1000000;
 				if (!quiet && advice_enabled(ADVICE_RESET_NO_REFRESH_WARNING) && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) {
@@ -501,7 +504,7 @@
 			free(ref);
 		}
 
-		if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK))
 			die(_("Could not write new index file."));
 	}
 
@@ -516,7 +519,7 @@
 	if (!pathspec.nr)
 		remove_branch_state(the_repository, 0);
 
-	discard_index(&the_index);
+	discard_index(the_repository->index);
 
 cleanup:
 	clear_pathspec(&pathspec);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 624182e..1e2919f 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -160,8 +160,9 @@
 			case 1: /* happy */
 				if (abbrev_ref) {
 					char *old = full;
-					full = shorten_unambiguous_ref(full,
-						abbrev_ref_strict);
+					full = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+									    full,
+									    abbrev_ref_strict);
 					free(old);
 				}
 				show_with_type(type, full);
@@ -599,9 +600,12 @@
 static void handle_ref_opt(const char *pattern, const char *prefix)
 {
 	if (pattern)
-		for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+		refs_for_each_glob_ref_in(get_main_ref_store(the_repository),
+					  show_reference, pattern, prefix,
+					  NULL);
 	else
-		for_each_ref_in(prefix, show_reference, NULL);
+		refs_for_each_ref_in(get_main_ref_store(the_repository),
+				     prefix, show_reference, NULL);
 	clear_ref_exclusions(&ref_excludes);
 }
 
@@ -687,7 +691,6 @@
 	const char *name = NULL;
 	struct object_context unused;
 	struct strbuf buf = STRBUF_INIT;
-	const int hexsz = the_hash_algo->hexsz;
 	int seen_end_of_options = 0;
 	enum format_type format = FORMAT_DEFAULT;
 
@@ -863,8 +866,8 @@
 				abbrev = strtoul(arg, NULL, 10);
 				if (abbrev < MINIMUM_ABBREV)
 					abbrev = MINIMUM_ABBREV;
-				else if (hexsz <= abbrev)
-					abbrev = hexsz;
+				else if ((int)the_hash_algo->hexsz <= abbrev)
+					abbrev = the_hash_algo->hexsz;
 				continue;
 			}
 			if (!strcmp(arg, "--sq")) {
@@ -898,7 +901,8 @@
 				continue;
 			}
 			if (!strcmp(arg, "--all")) {
-				for_each_ref(show_reference, NULL);
+				refs_for_each_ref(get_main_ref_store(the_repository),
+						  show_reference, NULL);
 				clear_ref_exclusions(&ref_excludes);
 				continue;
 			}
@@ -908,8 +912,14 @@
 				continue;
 			}
 			if (!strcmp(arg, "--bisect")) {
-				for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
-				for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
+				refs_for_each_fullref_in(get_main_ref_store(the_repository),
+							 "refs/bisect/bad",
+							 NULL, show_reference,
+							 NULL);
+				refs_for_each_fullref_in(get_main_ref_store(the_repository),
+							 "refs/bisect/good",
+							 NULL, anti_reference,
+							 NULL);
 				continue;
 			}
 			if (opt_with_value(arg, "--branches", &arg)) {
@@ -1049,8 +1059,8 @@
 			if (!strcmp(arg, "--shared-index-path")) {
 				if (repo_read_index(the_repository) < 0)
 					die(_("Could not read the index"));
-				if (the_index.split_index) {
-					const struct object_id *oid = &the_index.split_index->base_oid;
+				if (the_repository->index->split_index) {
+					const struct object_id *oid = &the_repository->index->split_index->base_oid;
 					const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
 					print_path(path, prefix, format, DEFAULT_RELATIVE);
 				}
diff --git a/builtin/rm.c b/builtin/rm.c
index fd130ce..d195c16 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "advice.h"
 #include "config.h"
@@ -41,8 +41,8 @@
 {
 	int i = -pos - 1;
 
-	while ((i < the_index.cache_nr) && !strcmp(the_index.cache[i]->name, path)) {
-		if (ce_stage(the_index.cache[i]) == 2)
+	while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
+		if (ce_stage(the_repository->index->cache[i]) == 2)
 			return i;
 		i++;
 	}
@@ -78,13 +78,13 @@
 		int pos;
 		const struct cache_entry *ce;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			pos = get_ours_cache_pos(name, pos);
 			if (pos < 0)
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (!S_ISGITLINK(ce->ce_mode) ||
 		    !file_exists(ce->name) ||
@@ -122,7 +122,7 @@
 		int local_changes = 0;
 		int staged_changes = 0;
 
-		pos = index_name_pos(&the_index, name, strlen(name));
+		pos = index_name_pos(the_repository->index, name, strlen(name));
 		if (pos < 0) {
 			/*
 			 * Skip unmerged entries except for populated submodules
@@ -132,11 +132,11 @@
 			if (pos < 0)
 				continue;
 
-			if (!S_ISGITLINK(the_index.cache[pos]->ce_mode) ||
+			if (!S_ISGITLINK(the_repository->index->cache[pos]->ce_mode) ||
 			    is_empty_dir(name))
 				continue;
 		}
-		ce = the_index.cache[pos];
+		ce = the_repository->index->cache[pos];
 
 		if (lstat(ce->name, &st) < 0) {
 			if (!is_missing_file_error(errno))
@@ -173,7 +173,7 @@
 		 * Is the index different from the file in the work tree?
 		 * If it's a submodule, is its work tree modified?
 		 */
-		if (ie_match_stat(&the_index, ce, &st, 0) ||
+		if (ie_match_stat(the_repository->index, ce, &st, 0) ||
 		    (S_ISGITLINK(ce->ce_mode) &&
 		     bad_to_remove_submodule(ce->name,
 				SUBMODULE_REMOVAL_DIE_ON_ERROR |
@@ -301,27 +301,27 @@
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
+	refresh_index(the_repository->index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
 
-	if (pathspec_needs_expanded_index(&the_index, &pathspec))
-		ensure_full_index(&the_index);
+	if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
+		ensure_full_index(the_repository->index);
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
 		if (!include_sparse &&
 		    (ce_skip_worktree(ce) ||
-		     !path_in_sparse_checkout(ce->name, &the_index)))
+		     !path_in_sparse_checkout(ce->name, the_repository->index)))
 			continue;
-		if (!ce_path_match(&the_index, ce, &pathspec, seen))
+		if (!ce_path_match(the_repository->index, ce, &pathspec, seen))
 			continue;
 		ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
 		list.entry[list.nr].name = xstrdup(ce->name);
 		list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode);
 		if (list.entry[list.nr++].is_submodule &&
-		    !is_staging_gitmodules_ok(&the_index))
+		    !is_staging_gitmodules_ok(the_repository->index))
 			die(_("please stage your changes to .gitmodules or stash them to proceed"));
 	}
 
@@ -391,7 +391,7 @@
 		if (!quiet)
 			printf("rm '%s'\n", path);
 
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die(_("git rm: unable to remove %s"), path);
 	}
 
@@ -432,10 +432,10 @@
 		}
 		strbuf_release(&buf);
 		if (gitmodules_modified)
-			stage_updated_gitmodules(&the_index);
+			stage_updated_gitmodules(the_repository->index);
 	}
 
-	if (write_locked_index(&the_index, &lock_file,
+	if (write_locked_index(the_repository->index, &lock_file,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("Unable to write new index file"));
 
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3c7cd2d..d4daf31 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -435,7 +435,7 @@
 		usage_with_options(shortlog_usage, options);
 	}
 
-	if (setup_revisions(argc, argv, &rev, NULL) != 1) {
+	if (!nongit && setup_revisions(argc, argv, &rev, NULL) != 1) {
 		error(_("unrecognized argument: %s"), argv[1]);
 		usage_with_options(shortlog_usage, options);
 	}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index b01ec76..d72f4cb 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -479,13 +479,15 @@
 	if (head) {
 		int orig_cnt = ref_name_cnt;
 
-		for_each_ref(append_head_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_head_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 	if (remotes) {
 		int orig_cnt = ref_name_cnt;
 
-		for_each_ref(append_remote_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_remote_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 }
@@ -549,7 +551,8 @@
 
 		match_ref_pattern = av;
 		match_ref_slash = count_slashes(av);
-		for_each_ref(append_matching_ref, NULL);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  append_matching_ref, NULL);
 		if (saved_matches == ref_name_cnt &&
 		    ref_name_cnt < MAX_REVS)
 			error(_("no matching refs with %s"), av);
@@ -740,9 +743,11 @@
 		if (ac == 0) {
 			static const char *fake_av[2];
 
-			fake_av[0] = resolve_refdup("HEAD",
-						    RESOLVE_REF_READING, &oid,
-						    NULL);
+			fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository),
+							 "HEAD",
+							 RESOLVE_REF_READING,
+							 &oid,
+							 NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
@@ -815,8 +820,9 @@
 			snarf_refs(all_heads, all_remotes);
 	}
 
-	head = resolve_refdup("HEAD", RESOLVE_REF_READING,
-			      &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   RESOLVE_REF_READING,
+				   &head_oid, NULL);
 
 	if (with_current_branch && head) {
 		int has_head = 0;
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 1c15421..3114bdc 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -50,7 +50,7 @@
 	if (!opts->deref_tags)
 		return;
 
-	if (!peel_iterated_oid(oid, &peeled)) {
+	if (!peel_iterated_oid(the_repository, oid, &peeled)) {
 		hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev);
 		printf("%s %s^{}\n", hex, refname);
 	}
@@ -129,7 +129,8 @@
 	char buf[1024];
 	int patternlen = opts->pattern ? strlen(opts->pattern) : 0;
 
-	for_each_ref(add_existing, &existing_refs);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_existing,
+			  &existing_refs);
 	while (fgets(buf, sizeof(buf), stdin)) {
 		char *ref;
 		int len = strlen(buf);
@@ -173,7 +174,7 @@
 		struct object_id oid;
 
 		if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
-		    !read_ref(*refs, &oid)) {
+		    !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) {
 			show_one(show_one_opts, *refs, &oid);
 		}
 		else if (!show_one_opts->quiet)
@@ -205,14 +206,20 @@
 		show_ref_data.patterns = patterns;
 
 	if (opts->show_head)
-		head_ref(show_ref, &show_ref_data);
+		refs_head_ref(get_main_ref_store(the_repository), show_ref,
+			      &show_ref_data);
 	if (opts->heads_only || opts->tags_only) {
 		if (opts->heads_only)
-			for_each_fullref_in("refs/heads/", show_ref, &show_ref_data);
+			refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						 "refs/heads/", NULL,
+						 show_ref, &show_ref_data);
 		if (opts->tags_only)
-			for_each_fullref_in("refs/tags/", show_ref, &show_ref_data);
+			refs_for_each_fullref_in(get_main_ref_store(the_repository),
+						 "refs/tags/", NULL, show_ref,
+						 &show_ref_data);
 	} else {
-		for_each_ref(show_ref, &show_ref_data);
+		refs_for_each_ref(get_main_ref_store(the_repository),
+				  show_ref, &show_ref_data);
 	}
 	if (!show_ref_data.found_match)
 		return 1;
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 0f52e25..3f2bfce 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -96,10 +96,11 @@
 			printf("\n");
 		}
 
-		return 0;
+		string_list_clear(&sl, 0);
+	} else {
+		write_patterns_to_file(stdout, &pl);
 	}
 
-	write_patterns_to_file(stdout, &pl);
 	clear_pattern_list(&pl);
 
 	return 0;
@@ -205,11 +206,13 @@
 	struct unpack_trees_options o;
 	struct lock_file lock_file = LOCK_INIT;
 	struct repository *r = the_repository;
+	struct pattern_list *old_pl;
 
 	/* If no branch has been checked out, there are no updates to make. */
 	if (is_index_unborn(r->index))
 		return UPDATE_SPARSITY_SUCCESS;
 
+	old_pl = r->index->sparse_checkout_patterns;
 	r->index->sparse_checkout_patterns = pl;
 
 	memset(&o, 0, sizeof(o));
@@ -241,7 +244,12 @@
 
 	clean_tracked_sparse_directories(r);
 
-	r->index->sparse_checkout_patterns = NULL;
+	if (r->index->sparse_checkout_patterns != pl) {
+		clear_pattern_list(r->index->sparse_checkout_patterns);
+		FREE_AND_NULL(r->index->sparse_checkout_patterns);
+	}
+	r->index->sparse_checkout_patterns = old_pl;
+
 	return result;
 }
 
@@ -311,6 +319,8 @@
 		fprintf(fp, "%s/\n", pattern);
 		free(pattern);
 	}
+
+	string_list_clear(&sl, 0);
 }
 
 static int write_patterns_and_update(struct pattern_list *pl)
@@ -440,7 +450,6 @@
 	char *sparse_filename;
 	int res;
 	struct object_id oid;
-	struct strbuf pattern = STRBUF_INIT;
 
 	static struct option builtin_sparse_checkout_init_options[] = {
 		OPT_BOOL(0, "cone", &init_opts.cone_mode,
@@ -471,6 +480,7 @@
 	/* If we already have a sparse-checkout file, use it. */
 	if (res >= 0) {
 		free(sparse_filename);
+		clear_pattern_list(&pl);
 		return update_working_directory(NULL);
 	}
 
@@ -491,10 +501,10 @@
 		return 0;
 	}
 
-	strbuf_addstr(&pattern, "/*");
-	add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
-	strbuf_addstr(&pattern, "!/*/");
-	add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0);
+	free(sparse_filename);
+
+	add_pattern("/*", empty_base, 0, &pl, 0);
+	add_pattern("!/*/", empty_base, 0, &pl, 0);
 	pl.use_cone_patterns = init_opts.cone_mode;
 
 	return write_patterns_and_update(&pl);
@@ -513,6 +523,7 @@
 		char *slash = strrchr(e->pattern, '/');
 		char *oldpattern = e->pattern;
 		size_t newlen;
+		struct pattern_entry *dup;
 
 		if (!slash || slash == e->pattern)
 			break;
@@ -523,8 +534,14 @@
 		e->pattern = xstrndup(oldpattern, newlen);
 		hashmap_entry_init(&e->ent, fspathhash(e->pattern));
 
-		if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL))
+		dup = hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL);
+		if (!dup) {
 			hashmap_add(&pl->parent_hashmap, &e->ent);
+		} else {
+			free(e->pattern);
+			free(e);
+			e = dup;
+		}
 	}
 }
 
@@ -581,15 +598,15 @@
 				strbuf_to_cone_pattern(&line, pl);
 			}
 		}
+		strbuf_release(&line);
 	} else {
 		if (file) {
 			struct strbuf line = STRBUF_INIT;
 
-			while (!strbuf_getline(&line, file)) {
-				size_t len;
-				char *buf = strbuf_detach(&line, &len);
-				add_pattern(buf, empty_base, 0, pl, 0);
-			}
+			while (!strbuf_getline(&line, file))
+				add_pattern(line.buf, empty_base, 0, pl, 0);
+
+			strbuf_release(&line);
 		} else {
 			for (i = 0; i < argc; i++)
 				add_pattern(argv[i], empty_base, 0, pl, 0);
@@ -891,7 +908,6 @@
 		OPT_END(),
 	};
 	struct pattern_list pl;
-	struct strbuf match_all = STRBUF_INIT;
 
 	/*
 	 * We do not exit early if !core_apply_sparse_checkout; due to the
@@ -917,8 +933,7 @@
 	pl.use_cone_patterns = 0;
 	core_apply_sparse_checkout = 1;
 
-	strbuf_addstr(&match_all, "/*");
-	add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0);
+	add_pattern("/*", empty_base, 0, &pl, 0);
 
 	prepare_repo_settings(the_repository);
 	the_repository->settings.sparse_index = 0;
diff --git a/builtin/stash.c b/builtin/stash.c
index 062be1f..7859bc0 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
@@ -196,7 +195,7 @@
 		commit = argv[0];
 
 	if (!commit) {
-		if (!ref_exists(ref_stash)) {
+		if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) {
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -244,7 +243,8 @@
 	if (repo_get_oid(the_repository, ref_stash, &obj))
 		return 0;
 
-	return delete_ref(NULL, ref_stash, &obj, 0);
+	return refs_delete_ref(get_main_ref_store(the_repository), NULL,
+			       ref_stash, &obj, 0);
 }
 
 static int clear_stash(int argc, const char **argv, const char *prefix)
@@ -273,7 +273,7 @@
 	struct lock_file lock_file = LOCK_INIT;
 
 	repo_read_index_preload(the_repository, NULL, 0);
-	if (refresh_index(&the_index, REFRESH_QUIET, NULL, NULL, NULL))
+	if (refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL))
 		return -1;
 
 	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -287,8 +287,8 @@
 	init_tree_desc(t, &tree->object.oid, tree->buffer, tree->size);
 
 	opts.head_idx = 1;
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
+	opts.src_index = the_repository->index;
+	opts.dst_index = the_repository->index;
 	opts.merge = 1;
 	opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
 	opts.update = update;
@@ -299,7 +299,7 @@
 	if (unpack_trees(nr_trees, t, &opts))
 		return -1;
 
-	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 		return error(_("unable to write new index file"));
 
 	return 0;
@@ -430,7 +430,7 @@
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
-	state.istate = &the_index;
+	state.istate = the_repository->index;
 
 	/*
 	 * Step 1: get a difference between orig_tree (which corresponding
@@ -454,7 +454,7 @@
 
 		/* Look up the path's position in the current index. */
 		p = diff_queued_diff.queue[i];
-		pos = index_name_pos(&the_index, p->two->path,
+		pos = index_name_pos(the_repository->index, p->two->path,
 				     strlen(p->two->path));
 
 		/*
@@ -465,10 +465,10 @@
 		 * path, but left it out of the working tree, then clear the
 		 * SKIP_WORKTREE bit and write it to the working tree.
 		 */
-		if (pos >= 0 && ce_skip_worktree(the_index.cache[pos])) {
+		if (pos >= 0 && ce_skip_worktree(the_repository->index->cache[pos])) {
 			struct stat st;
 
-			ce = the_index.cache[pos];
+			ce = the_repository->index->cache[pos];
 			if (!lstat(ce->name, &st)) {
 				/* Conflicting path present; relocate it */
 				struct strbuf new_path = STRBUF_INIT;
@@ -504,12 +504,12 @@
 			if (pos < 0)
 				option = ADD_CACHE_OK_TO_ADD;
 
-			ce = make_cache_entry(&the_index,
+			ce = make_cache_entry(the_repository->index,
 					      p->one->mode,
 					      &p->one->oid,
 					      p->one->path,
 					      0, 0);
-			add_index_entry(&the_index, ce, option);
+			add_index_entry(the_repository->index, ce, option);
 		}
 	}
 	diff_flush(&diff_opts);
@@ -518,7 +518,7 @@
 	 * Step 4: write the new index to disk
 	 */
 	repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
-	if (write_locked_index(&the_index, &lock,
+	if (write_locked_index(the_repository->index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("could not write index"));
 }
@@ -539,7 +539,7 @@
 					 NULL, NULL, NULL))
 		return error(_("could not write index"));
 
-	if (write_index_as_tree(&c_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&c_tree, the_repository->index, get_index_file(), 0,
 				NULL))
 		return error(_("cannot apply a stash in the middle of a merge"));
 
@@ -562,14 +562,14 @@
 				return error(_("conflicts in index. "
 					       "Try without --index."));
 
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
-			if (write_index_as_tree(&index_tree, &the_index,
+			if (write_index_as_tree(&index_tree, the_repository->index,
 						get_index_file(), 0, NULL))
 				return error(_("could not save index tree"));
 
 			reset_head();
-			discard_index(&the_index);
+			discard_index(the_repository->index);
 			repo_read_index(the_repository);
 		}
 	}
@@ -687,7 +687,8 @@
 
 static int reflog_is_empty(const char *refname)
 {
-	return !for_each_reflog_ent(refname, reject_reflog_ent, NULL);
+	return !refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+					 refname, reject_reflog_ent, NULL);
 }
 
 static int do_drop_stash(struct stash_info *info, int quiet)
@@ -824,7 +825,7 @@
 			     git_stash_list_usage,
 			     PARSE_OPT_KEEP_UNKNOWN_OPT);
 
-	if (!ref_exists(ref_stash))
+	if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash))
 		return 0;
 
 	cp.git_cmd = 1;
@@ -875,8 +876,8 @@
 	}
 
 	unpack_tree_opt.head_idx = -1;
-	unpack_tree_opt.src_index = &the_index;
-	unpack_tree_opt.dst_index = &the_index;
+	unpack_tree_opt.src_index = the_repository->index;
+	unpack_tree_opt.dst_index = the_repository->index;
 	unpack_tree_opt.merge = 1;
 	unpack_tree_opt.fn = stash_worktree_untracked_merge;
 
@@ -998,10 +999,10 @@
 	if (!stash_msg)
 		stash_msg = "Created via \"git stash store\".";
 
-	if (update_ref(stash_msg, ref_stash, w_commit, NULL,
-		       REF_FORCE_CREATE_REFLOG,
-		       quiet ? UPDATE_REFS_QUIET_ON_ERR :
-		       UPDATE_REFS_MSG_ON_ERR)) {
+	if (refs_update_ref(get_main_ref_store(the_repository), stash_msg, ref_stash, w_commit, NULL,
+			    REF_FORCE_CREATE_REFLOG,
+			    quiet ? UPDATE_REFS_QUIET_ON_ERR :
+			    UPDATE_REFS_MSG_ON_ERR)) {
 		if (!quiet) {
 			fprintf_ln(stderr, _("Cannot update %s with %s"),
 				   ref_stash, oid_to_hex(w_commit));
@@ -1205,8 +1206,8 @@
 	}
 
 	cp_diff_tree.git_cmd = 1;
-	strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
-		     oid_to_hex(&info->w_tree), "--", NULL);
+	strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary",
+		     "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL);
 	if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
 		ret = -1;
 		goto done;
@@ -1384,7 +1385,8 @@
 		goto done;
 	}
 
-	branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+	branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					     "HEAD", 0, NULL, &flags);
 	if (flags & REF_ISSYMREF)
 		skip_prefix(branch_ref, "refs/heads/", &branch_name);
 	head_short_sha1 = repo_find_unique_abbrev(the_repository,
@@ -1395,7 +1397,7 @@
 
 	strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
 	commit_list_insert(head_commit, &parents);
-	if (write_index_as_tree(&info->i_tree, &the_index, get_index_file(), 0,
+	if (write_index_as_tree(&info->i_tree, the_repository->index, get_index_file(), 0,
 				NULL) ||
 	    commit_tree(commit_tree_label.buf, commit_tree_label.len,
 			&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
@@ -1540,9 +1542,9 @@
 		char *ps_matched = xcalloc(ps->nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
-		for (i = 0; i < the_index.cache_nr; i++)
-			ce_path_match(&the_index, the_index.cache[i], ps,
+		ensure_full_index(the_repository->index);
+		for (i = 0; i < the_repository->index->cache_nr; i++)
+			ce_path_match(the_repository->index, the_repository->index->cache[i], ps,
 				      ps_matched);
 
 		if (report_path_error(ps_matched, ps)) {
@@ -1566,7 +1568,7 @@
 		goto done;
 	}
 
-	if (!reflog_exists(ref_stash) && do_clear_stash()) {
+	if (!refs_reflog_exists(get_main_ref_store(the_repository), ref_stash) && do_clear_stash()) {
 		ret = -1;
 		if (!quiet)
 			fprintf_ln(stderr, _("Cannot initialize stash"));
@@ -1612,7 +1614,7 @@
 				goto done;
 			}
 		}
-		discard_index(&the_index);
+		discard_index(the_repository->index);
 		if (ps->nr) {
 			struct child_process cp_add = CHILD_PROCESS_INIT;
 			struct child_process cp_diff = CHILD_PROCESS_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e4e18ad..880ab44 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1,4 +1,3 @@
-#define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "abspath.h"
 #include "environment.h"
@@ -207,18 +206,18 @@
 	if (repo_read_index(the_repository) < 0)
 		die(_("index file corrupt"));
 
-	for (i = 0; i < the_index.cache_nr; i++) {
-		const struct cache_entry *ce = the_index.cache[i];
+	for (i = 0; i < the_repository->index->cache_nr; i++) {
+		const struct cache_entry *ce = the_repository->index->cache[i];
 
-		if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce),
+		if (!match_pathspec(the_repository->index, pathspec, ce->name, ce_namelen(ce),
 				    0, ps_matched, 1) ||
 		    !S_ISGITLINK(ce->ce_mode))
 			continue;
 
 		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
 		list->entries[list->nr++] = ce;
-		while (i + 1 < the_index.cache_nr &&
-		       !strcmp(ce->name, the_index.cache[i + 1]->name))
+		while (i + 1 < the_repository->index->cache_nr &&
+		       !strcmp(ce->name, the_repository->index->cache[i + 1]->name))
 			/*
 			 * Skip entries with the same name in different stages
 			 * to make sure an entry is returned only once.
@@ -257,11 +256,9 @@
 
 static char *get_up_path(const char *path)
 {
-	int i;
 	struct strbuf sb = STRBUF_INIT;
 
-	for (i = count_slashes(path); i; i--)
-		strbuf_addstr(&sb, "../");
+	strbuf_addstrings(&sb, "../", count_slashes(path));
 
 	/*
 	 * Check if 'path' ends with slash or not
@@ -303,6 +300,9 @@
 	struct child_process cp = CHILD_PROCESS_INIT;
 	char *displaypath;
 
+	if (validate_submodule_path(path) < 0)
+		exit(128);
+
 	displaypath = get_submodule_displaypath(path, info->prefix,
 						info->super_prefix);
 
@@ -634,6 +634,9 @@
 		.free_removed_argv_elements = 1,
 	};
 
+	if (validate_submodule_path(path) < 0)
+		exit(128);
+
 	if (!submodule_from_path(the_repository, null_oid(), path))
 		die(_("no submodule mapping found in .gitmodules for path '%s'"),
 		      path);
@@ -674,7 +677,8 @@
 			     displaypath);
 	} else if (!(flags & OPT_CACHED)) {
 		struct object_id oid;
-		struct ref_store *refs = get_submodule_ref_store(path);
+		struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+								      path);
 
 		if (!refs) {
 			print_status(flags, '-', path, ce_oid, displaypath);
@@ -898,7 +902,8 @@
 
 	if (!info->cached && oideq(&p->oid_dst, null_oid())) {
 		if (S_ISGITLINK(p->mod_dst)) {
-			struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+			struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
+									      p->sm_path);
 
 			if (refs)
 				refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
@@ -907,7 +912,7 @@
 			int fd = open(p->sm_path, O_RDONLY);
 
 			if (fd < 0 || fstat(fd, &st) < 0 ||
-			    index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+			    index_fd(the_repository->index, &p->oid_dst, fd, &st, OBJ_BLOB,
 				     p->sm_path, 0))
 				error(_("couldn't hash object from '%s'"), p->sm_path);
 		} else {
@@ -1238,6 +1243,9 @@
 	if (!is_submodule_active(the_repository, path))
 		return;
 
+	if (validate_submodule_path(path) < 0)
+		exit(128);
+
 	sub = submodule_from_path(the_repository, null_oid(), path);
 
 	if (sub && sub->url) {
@@ -1381,6 +1389,9 @@
 	struct strbuf sb_config = STRBUF_INIT;
 	char *sub_git_dir = xstrfmt("%s/.git", path);
 
+	if (validate_submodule_path(path) < 0)
+		exit(128);
+
 	sub = submodule_from_path(the_repository, null_oid(), path);
 
 	if (!sub || !sub->name)
@@ -1662,16 +1673,42 @@
 	return sm_gitdir;
 }
 
+static int dir_contains_only_dotgit(const char *path)
+{
+	DIR *dir = opendir(path);
+	struct dirent *e;
+	int ret = 1;
+
+	if (!dir)
+		return 0;
+
+	e = readdir_skip_dot_and_dotdot(dir);
+	if (!e)
+		ret = 0;
+	else if (strcmp(DEFAULT_GIT_DIR_ENVIRONMENT, e->d_name) ||
+		 (e = readdir_skip_dot_and_dotdot(dir))) {
+		error("unexpected item '%s' in '%s'", e->d_name, path);
+		ret = 0;
+	}
+
+	closedir(dir);
+	return ret;
+}
+
 static int clone_submodule(const struct module_clone_data *clone_data,
 			   struct string_list *reference)
 {
 	char *p;
 	char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
 	char *sm_alternate = NULL, *error_strategy = NULL;
+	struct stat st;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	const char *clone_data_path = clone_data->path;
 	char *to_free = NULL;
 
+	if (validate_submodule_path(clone_data_path) < 0)
+		exit(128);
+
 	if (!is_absolute_path(clone_data->path))
 		clone_data_path = to_free = xstrfmt("%s/%s", get_git_work_tree(),
 						    clone_data->path);
@@ -1681,6 +1718,10 @@
 		      "git dir"), sm_gitdir);
 
 	if (!file_exists(sm_gitdir)) {
+		if (clone_data->require_init && !stat(clone_data_path, &st) &&
+		    !is_empty_dir(clone_data_path))
+			die(_("directory not empty: '%s'"), clone_data_path);
+
 		if (safe_create_leading_directories_const(sm_gitdir) < 0)
 			die(_("could not create directory '%s'"), sm_gitdir);
 
@@ -1725,10 +1766,18 @@
 		if(run_command(&cp))
 			die(_("clone of '%s' into submodule path '%s' failed"),
 			    clone_data->url, clone_data_path);
+
+		if (clone_data->require_init && !stat(clone_data_path, &st) &&
+		    !dir_contains_only_dotgit(clone_data_path)) {
+			char *dot_git = xstrfmt("%s/.git", clone_data_path);
+			unlink(dot_git);
+			free(dot_git);
+			die(_("directory not empty: '%s'"), clone_data_path);
+		}
 	} else {
 		char *path;
 
-		if (clone_data->require_init && !access(clone_data_path, X_OK) &&
+		if (clone_data->require_init && !stat(clone_data_path, &st) &&
 		    !is_empty_dir(clone_data_path))
 			die(_("directory not empty: '%s'"), clone_data_path);
 		if (safe_create_leading_directories_const(clone_data_path) < 0)
@@ -1738,6 +1787,23 @@
 		free(path);
 	}
 
+	/*
+	 * We already performed this check at the beginning of this function,
+	 * before cloning the objects. This tries to detect racy behavior e.g.
+	 * in parallel clones, where another process could easily have made the
+	 * gitdir nested _after_ it was created.
+	 *
+	 * To prevent further harm coming from this unintentionally-nested
+	 * gitdir, let's disable it by deleting the `HEAD` file.
+	 */
+	if (validate_submodule_git_dir(sm_gitdir, clone_data->name) < 0) {
+		char *head = xstrfmt("%s/HEAD", sm_gitdir);
+		unlink(head);
+		free(head);
+		die(_("refusing to create/use '%s' in another submodule's "
+		      "git dir"), sm_gitdir);
+	}
+
 	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
 
 	p = git_pathdup_submodule(clone_data_path, "config");
@@ -2390,7 +2456,9 @@
 	}
 
 	if (!strcmp(*branch, ".")) {
-		const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+		const char *refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+							      "HEAD", 0, NULL,
+							      NULL);
 
 		if (!refname)
 			return die_message(_("No such ref: %s"), "HEAD");
@@ -2517,6 +2585,9 @@
 {
 	int ret;
 
+	if (validate_submodule_path(update_data->sm_path) < 0)
+		return -1;
+
 	ret = determine_submodule_update_strategy(the_repository,
 						  update_data->just_cloned,
 						  update_data->sm_path,
@@ -2527,7 +2598,8 @@
 
 	if (update_data->just_cloned)
 		oidcpy(&update_data->suboid, null_oid());
-	else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
+	else if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+					  "HEAD", &update_data->suboid))
 		return die_message(_("Unable to find current revision in submodule path '%s'"),
 				   update_data->displaypath);
 
@@ -2554,7 +2626,8 @@
 						   update_data->sm_path);
 		}
 
-		if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
+		if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
+					     remote_ref, &update_data->oid))
 			return die_message(_("Unable to find %s revision in submodule path '%s'"),
 					   remote_ref, update_data->sm_path);
 
@@ -2624,12 +2697,21 @@
 
 	for (i = 0; i < suc.update_clone_nr; i++) {
 		struct update_clone_data ucd = suc.update_clone[i];
-		int code;
+		int code = 128;
 
 		oidcpy(&update_data->oid, &ucd.oid);
 		update_data->just_cloned = ucd.just_cloned;
 		update_data->sm_path = ucd.sub->path;
 
+		/*
+		 * Verify that the submodule path does not contain any
+		 * symlinks; if it does, it might have been tampered with.
+		 * TODO: allow exempting it via
+		 * `safe.submodule.path` or something
+		 */
+		if (validate_submodule_path(update_data->sm_path) < 0)
+			goto fail;
+
 		code = ensure_core_worktree(update_data->sm_path);
 		if (code)
 			goto fail;
@@ -2796,7 +2878,8 @@
 	argv++;
 	argc--;
 	/* Get the submodule's head ref and determine if it is detached */
-	head = resolve_refdup("HEAD", 0, &head_oid, NULL);
+	head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD",
+				   0, &head_oid, NULL);
 	if (!head)
 		die(_("Failed to resolve HEAD as a valid ref."));
 	if (!strcmp(head, "HEAD"))
@@ -3243,21 +3326,21 @@
 		char *ps_matched = xcalloc(ps.nr, 1);
 
 		/* TODO: audit for interaction with sparse-index. */
-		ensure_full_index(&the_index);
+		ensure_full_index(the_repository->index);
 
 		/*
 		 * Since there is only one pathspec, we just need to
 		 * check ps_matched[0] to know if a cache entry matched.
 		 */
-		for (i = 0; i < the_index.cache_nr; i++) {
-			ce_path_match(&the_index, the_index.cache[i], &ps,
+		for (i = 0; i < the_repository->index->cache_nr; i++) {
+			ce_path_match(the_repository->index, the_repository->index->cache[i], &ps,
 				      ps_matched);
 
 			if (ps_matched[0]) {
 				if (!force)
 					die(_("'%s' already exists in the index"),
 					    path);
-				if (!S_ISGITLINK(the_index.cache[i]->ce_mode))
+				if (!S_ISGITLINK(the_repository->index->cache[i]->ce_mode))
 					die(_("'%s' already exists in the index "
 					      "and is not a submodule"), path);
 				break;
@@ -3274,7 +3357,7 @@
 	strbuf_addstr(&sb, path);
 	if (is_nonbare_repository_dir(&sb)) {
 		struct object_id oid;
-		if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+		if (repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid) < 0)
 			die(_("'%s' does not have a commit checked out"), path);
 	}
 	strbuf_release(&sb);
@@ -3356,6 +3439,9 @@
 	normalize_path_copy(add_data.sm_path, add_data.sm_path);
 	strip_dir_trailing_slashes(add_data.sm_path);
 
+	if (validate_submodule_path(add_data.sm_path) < 0)
+		exit(128);
+
 	die_on_index_match(add_data.sm_path, force);
 	die_on_repo_without_commits(add_data.sm_path);
 
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index c9defe4..81abdd1 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -18,7 +18,8 @@
 	const char *refname;
 
 	resolve_flags = (recurse ? 0 : RESOLVE_REF_NO_RECURSE);
-	refname = resolve_ref_unsafe(HEAD, resolve_flags, NULL, &flag);
+	refname = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					  HEAD, resolve_flags, NULL, &flag);
 
 	if (!refname)
 		die("No such ref: %s", HEAD);
@@ -31,7 +32,9 @@
 	if (print) {
 		char *to_free = NULL;
 		if (shorten)
-			refname = to_free = shorten_unambiguous_ref(refname, 0);
+			refname = to_free = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+									 refname,
+									 0);
 		puts(refname);
 		free(to_free);
 	}
@@ -66,7 +69,8 @@
 			die("Cannot delete %s, not a symbolic ref", argv[0]);
 		if (!strcmp(argv[0], "HEAD"))
 			die("deleting '%s' is not allowed", argv[0]);
-		return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF);
+		return refs_delete_ref(get_main_ref_store(the_repository),
+				       NULL, argv[0], NULL, REF_NO_DEREF);
 	}
 
 	switch (argc) {
@@ -79,7 +83,8 @@
 			die("Refusing to point HEAD outside of refs/");
 		if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
 			die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
-		ret = !!create_symref(argv[0], argv[1], msg);
+		ret = !!refs_update_symref(get_main_ref_store(the_repository),
+					   argv[0], argv[1], msg);
 		break;
 	default:
 		usage_with_options(git_symbolic_ref_usage, options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 9a33cb5..6e2c0cf 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,9 +28,11 @@
 #include "date.h"
 #include "write-or-die.h"
 #include "object-file-convert.h"
+#include "trailer.h"
 
 static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] [-e]\n"
+	   "        [(--trailer <token>[(=|:)<value>])...]\n"
 	   "        <tagname> [<commit> | <object>]"),
 	N_("git tag -d <tagname>..."),
 	N_("git tag [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]\n"
@@ -87,7 +89,7 @@
 	for (p = argv; *p; p++) {
 		strbuf_reset(&ref);
 		strbuf_addf(&ref, "refs/tags/%s", *p);
-		if (read_ref(ref.buf, &oid)) {
+		if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
 			error(_("tag '%s' not found."), *p);
 			had_error = 1;
 			continue;
@@ -116,13 +118,13 @@
 	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))
+	if (refs_delete_refs(get_main_ref_store(the_repository), 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))
+		if (!refs_ref_exists(get_main_ref_store(the_repository), name))
 			printf(_("Deleted tag '%s' (was %s)\n"),
 				item->string + 10,
 				repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV));
@@ -290,10 +292,12 @@
 static void create_tag(const struct object_id *object, const char *object_ref,
 		       const char *tag,
 		       struct strbuf *buf, struct create_tag_options *opt,
-		       struct object_id *prev, struct object_id *result, char *path)
+		       struct object_id *prev, struct object_id *result,
+		       struct strvec *trailer_args, char *path)
 {
 	enum object_type type;
 	struct strbuf header = STRBUF_INIT;
+	int should_edit;
 
 	type = oid_object_info(the_repository, object, NULL);
 	if (type <= OBJ_NONE)
@@ -313,13 +317,15 @@
 		    tag,
 		    git_committer_info(IDENT_STRICT));
 
-	if (!opt->message_given || opt->use_editor) {
+	should_edit = opt->use_editor || !opt->message_given;
+	if (should_edit || trailer_args->nr) {
 		int fd;
 
 		/* write the template message before editing: */
 		fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
-		if (opt->message_given) {
+		if (opt->message_given && buf->len) {
+			strbuf_complete(buf, '\n');
 			write_or_die(fd, buf->buf, buf->len);
 			strbuf_reset(buf);
 		} else if (!is_null_oid(prev)) {
@@ -338,10 +344,19 @@
 		}
 		close(fd);
 
-		if (launch_editor(path, buf, NULL)) {
-			fprintf(stderr,
-			_("Please supply the message using either -m or -F option.\n"));
-			exit(1);
+		if (trailer_args->nr && amend_file_with_trailers(path, trailer_args))
+			die(_("unable to pass trailers to --trailers"));
+
+		if (should_edit) {
+			if (launch_editor(path, buf, NULL)) {
+				fprintf(stderr,
+					_("Please supply the message using either -m or -F option.\n"));
+				exit(1);
+			}
+		} else if (trailer_args->nr) {
+			strbuf_reset(buf);
+			if (strbuf_read_file(buf, path, 0) < 0)
+				die_errno(_("failed to read '%s'"), path);
 		}
 	}
 
@@ -463,6 +478,7 @@
 	struct ref_sorting *sorting;
 	struct string_list sorting_options = STRING_LIST_INIT_DUP;
 	struct ref_format format = REF_FORMAT_INIT;
+	struct strvec trailer_args = STRVEC_INIT;
 	int icase = 0;
 	int edit_flag = 0;
 	struct option options[] = {
@@ -479,6 +495,8 @@
 		OPT_CALLBACK_F('m', "message", &msg, N_("message"),
 			       N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
 		OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+		OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"),
+				  N_("add custom trailer(s)"), PARSE_OPT_NONEG),
 		OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
 		OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
 		OPT_CLEANUP(&cleanup_arg),
@@ -548,7 +566,8 @@
 		opt.sign = 1;
 		set_signing_key(keyid);
 	}
-	create_tag_object = (opt.sign || annotate || msg.given || msgfile);
+	create_tag_object = (opt.sign || annotate || msg.given || msgfile ||
+			     edit_flag || trailer_args.nr);
 
 	if ((create_tag_object || force) && (cmdmode != 0))
 		usage_with_options(git_tag_usage, options);
@@ -630,7 +649,7 @@
 	if (strbuf_check_tag_ref(&ref, tag))
 		die(_("'%s' is not a valid tag name."), tag);
 
-	if (read_ref(ref.buf, &prev))
+	if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &prev))
 		oidclr(&prev);
 	else if (!force)
 		die(_("tag '%s' already exists"), tag);
@@ -654,12 +673,14 @@
 			opt.sign = 1;
 		path = git_pathdup("TAG_EDITMSG");
 		create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
-			   path);
+			   &trailer_args, path);
 	}
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction ||
 	    ref_transaction_update(transaction, ref.buf, &object, &prev,
+				   NULL, NULL,
 				   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 				   reflog_msg.buf, &err) ||
 	    ref_transaction_commit(transaction, &err)) {
@@ -686,6 +707,7 @@
 	strbuf_release(&reflog_msg);
 	strbuf_release(&msg.buf);
 	strbuf_release(&err);
+	strvec_clear(&trailer_args);
 	free(msgfile);
 	return ret;
 }
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 7bcaa14..d343416 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "bulk-checkin.h"
 #include "config.h"
@@ -247,16 +247,16 @@
 static int mark_ce_flags(const char *path, int flag, int mark)
 {
 	int namelen = strlen(path);
-	int pos = index_name_pos(&the_index, path, namelen);
+	int pos = index_name_pos(the_repository->index, path, namelen);
 	if (0 <= pos) {
-		mark_fsmonitor_invalid(&the_index, the_index.cache[pos]);
+		mark_fsmonitor_invalid(the_repository->index, the_repository->index->cache[pos]);
 		if (mark)
-			the_index.cache[pos]->ce_flags |= flag;
+			the_repository->index->cache[pos]->ce_flags |= flag;
 		else
-			the_index.cache[pos]->ce_flags &= ~flag;
-		the_index.cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
-		cache_tree_invalidate_path(&the_index, path);
-		the_index.cache_changed |= CE_ENTRY_CHANGED;
+			the_repository->index->cache[pos]->ce_flags &= ~flag;
+		the_repository->index->cache[pos]->ce_flags |= CE_UPDATE_IN_BASE;
+		cache_tree_invalidate_path(the_repository->index, path);
+		the_repository->index->cache_changed |= CE_ENTRY_CHANGED;
 		return 0;
 	}
 	return -1;
@@ -266,7 +266,7 @@
 {
 	if (!allow_remove)
 		return error("%s: does not exist and --remove not passed", path);
-	if (remove_file_from_index(&the_index, path))
+	if (remove_file_from_index(the_repository->index, path))
 		return error("%s: cannot remove from the index", path);
 	return 0;
 }
@@ -291,24 +291,24 @@
 	struct cache_entry *ce;
 
 	/* Was the old index entry already up-to-date? */
-	if (old && !ce_stage(old) && !ie_match_stat(&the_index, old, st, 0))
+	if (old && !ce_stage(old) && !ie_match_stat(the_repository->index, old, st, 0))
 		return 0;
 
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 	memcpy(ce->name, path, len);
 	ce->ce_flags = create_ce_flags(0);
 	ce->ce_namelen = len;
-	fill_stat_cache_info(&the_index, ce, st);
+	fill_stat_cache_info(the_repository->index, ce, st);
 	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
-	if (index_path(&the_index, &ce->oid, path, st,
+	if (index_path(the_repository->index, &ce->oid, path, st,
 		       info_only ? 0 : HASH_WRITE_OBJECT)) {
 		discard_cache_entry(ce);
 		return -1;
 	}
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option)) {
+	if (add_index_entry(the_repository->index, ce, option)) {
 		discard_cache_entry(ce);
 		return error("%s: cannot add to the index - missing --add option?", path);
 	}
@@ -341,15 +341,16 @@
 static int process_directory(const char *path, int len, struct stat *st)
 {
 	struct object_id oid;
-	int pos = index_name_pos(&the_index, path, len);
+	int pos = index_name_pos(the_repository->index, path, len);
 
 	/* Exact match: file or existing gitlink */
 	if (pos >= 0) {
-		const struct cache_entry *ce = the_index.cache[pos];
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		if (S_ISGITLINK(ce->ce_mode)) {
 
 			/* Do nothing to the index if there is no HEAD! */
-			if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+			if (repo_resolve_gitlink_ref(the_repository, path,
+						     "HEAD", &oid) < 0)
 				return 0;
 
 			return add_one_path(ce, path, len, st);
@@ -360,8 +361,8 @@
 
 	/* Inexact match: is there perhaps a subdirectory match? */
 	pos = -pos-1;
-	while (pos < the_index.cache_nr) {
-		const struct cache_entry *ce = the_index.cache[pos++];
+	while (pos < the_repository->index->cache_nr) {
+		const struct cache_entry *ce = the_repository->index->cache[pos++];
 
 		if (strncmp(ce->name, path, len))
 			break;
@@ -375,7 +376,7 @@
 	}
 
 	/* No match - should we add it as a gitlink? */
-	if (!resolve_gitlink_ref(path, "HEAD", &oid))
+	if (!repo_resolve_gitlink_ref(the_repository, path, "HEAD", &oid))
 		return add_one_path(NULL, path, len, st);
 
 	/* Error out. */
@@ -391,8 +392,8 @@
 	if (has_symlink_leading_path(path, len))
 		return error("'%s' is beyond a symbolic link", path);
 
-	pos = index_name_pos(&the_index, path, len);
-	ce = pos < 0 ? NULL : the_index.cache[pos];
+	pos = index_name_pos(the_repository->index, path, len);
+	ce = pos < 0 ? NULL : the_repository->index->cache[pos];
 	if (ce && ce_skip_worktree(ce)) {
 		/*
 		 * working directory version is assumed "good"
@@ -400,7 +401,7 @@
 		 * On the other hand, removing it from index should work
 		 */
 		if (!ignore_skip_worktree_entries && allow_remove &&
-		    remove_file_from_index(&the_index, path))
+		    remove_file_from_index(the_repository->index, path))
 			return error("%s: cannot remove from the index", path);
 		return 0;
 	}
@@ -428,7 +429,7 @@
 		return error("Invalid path '%s'", path);
 
 	len = strlen(path);
-	ce = make_empty_cache_entry(&the_index, len);
+	ce = make_empty_cache_entry(the_repository->index, len);
 
 	oidcpy(&ce->oid, oid);
 	memcpy(ce->name, path, len);
@@ -439,7 +440,7 @@
 		ce->ce_flags |= CE_VALID;
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	if (add_index_entry(&the_index, ce, option))
+	if (add_index_entry(the_repository->index, ce, option))
 		return error("%s: cannot add to the index - missing --add option?",
 			     path);
 	report("add '%s'", path);
@@ -451,11 +452,11 @@
 	int pos;
 	struct cache_entry *ce;
 
-	pos = index_name_pos(&the_index, path, strlen(path));
+	pos = index_name_pos(the_repository->index, path, strlen(path));
 	if (pos < 0)
 		goto fail;
-	ce = the_index.cache[pos];
-	if (chmod_index_entry(&the_index, ce, flip) < 0)
+	ce = the_repository->index->cache[pos];
+	if (chmod_index_entry(the_repository->index, ce, flip) < 0)
 		goto fail;
 
 	report("chmod %cx '%s'", flip, path);
@@ -498,7 +499,7 @@
 	}
 
 	if (force_remove) {
-		if (remove_file_from_index(&the_index, path))
+		if (remove_file_from_index(the_repository->index, path))
 			die("git update-index: unable to remove %s", path);
 		report("remove '%s'", path);
 		return;
@@ -581,7 +582,7 @@
 
 		if (!mode) {
 			/* mode == 0 means there is no such path -- remove */
-			if (remove_file_from_index(&the_index, path_name))
+			if (remove_file_from_index(the_repository->index, path_name))
 				die("git update-index: unable to remove %s",
 				    ptr);
 		}
@@ -622,12 +623,12 @@
 			error("%s: not in %s branch.", path, which);
 		return NULL;
 	}
-	if (!the_index.sparse_index && mode == S_IFDIR) {
+	if (!the_repository->index->sparse_index && mode == S_IFDIR) {
 		if (which)
 			error("%s: not a blob in %s branch.", path, which);
 		return NULL;
 	}
-	ce = make_empty_cache_entry(&the_index, namelen);
+	ce = make_empty_cache_entry(the_repository->index, namelen);
 
 	oidcpy(&ce->oid, &oid);
 	memcpy(ce->name, path, namelen);
@@ -642,12 +643,12 @@
 	struct string_list_item *item;
 	int res = 0;
 
-	if (!the_index.resolve_undo)
+	if (!the_repository->index->resolve_undo)
 		return res;
-	item = string_list_lookup(the_index.resolve_undo, path);
+	item = string_list_lookup(the_repository->index->resolve_undo, path);
 	if (!item)
 		return res; /* no resolve-undo record for the path */
-	res = unmerge_index_entry(&the_index, path, item->util, 0);
+	res = unmerge_index_entry(the_repository->index, path, item->util, 0);
 	FREE_AND_NULL(item->util);
 	return res;
 }
@@ -682,19 +683,19 @@
 		       PATHSPEC_PREFER_CWD,
 		       prefix, paths);
 
-	if (read_ref("HEAD", &head_oid))
+	if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &head_oid))
 		/* If there is no HEAD, that means it is an initial
 		 * commit.  Update everything in the index.
 		 */
 		has_head = 0;
  redo:
-	for (pos = 0; pos < the_index.cache_nr; pos++) {
-		const struct cache_entry *ce = the_index.cache[pos];
+	for (pos = 0; pos < the_repository->index->cache_nr; pos++) {
+		const struct cache_entry *ce = the_repository->index->cache[pos];
 		struct cache_entry *old = NULL;
 		int save_nr;
 		char *path;
 
-		if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL))
+		if (ce_stage(ce) || !ce_path_match(the_repository->index, ce, &pathspec, NULL))
 			continue;
 		if (has_head)
 			old = read_one_ent(NULL, &head_oid,
@@ -710,7 +711,7 @@
 		 * to process each path individually
 		 */
 		if (S_ISSPARSEDIR(ce->ce_mode)) {
-			ensure_full_index(&the_index);
+			ensure_full_index(the_repository->index);
 			goto redo;
 		}
 
@@ -718,12 +719,12 @@
 		 * path anymore, in which case, under 'allow_remove',
 		 * or worse yet 'allow_replace', active_nr may decrease.
 		 */
-		save_nr = the_index.cache_nr;
+		save_nr = the_repository->index->cache_nr;
 		path = xstrdup(ce->name);
 		update_one(path);
 		free(path);
 		discard_cache_entry(old);
-		if (save_nr != the_index.cache_nr)
+		if (save_nr != the_repository->index->cache_nr)
 			goto redo;
 	}
 	clear_pathspec(&pathspec);
@@ -739,9 +740,9 @@
 {
 	setup_work_tree();
 	repo_read_index(the_repository);
-	*o->has_errors |= refresh_index(&the_index, o->flags | flag, NULL,
+	*o->has_errors |= refresh_index(the_repository->index, o->flags | flag, NULL,
 					NULL, NULL);
-	if (has_racy_timestamp(&the_index)) {
+	if (has_racy_timestamp(the_repository->index)) {
 		/*
 		 * Even if nothing else has changed, updating the file
 		 * increases the chance that racy timestamps become
@@ -750,7 +751,7 @@
 		 * refresh_index() as these are no actual errors.
 		 * cmd_status() does the same.
 		 */
-		the_index.cache_changed |= SOMETHING_CHANGED;
+		the_repository->index->cache_changed |= SOMETHING_CHANGED;
 	}
 	return 0;
 }
@@ -787,7 +788,7 @@
 {
 	BUG_ON_OPT_NEG(unset);
 	BUG_ON_OPT_ARG(arg);
-	resolve_undo_clear_index(&the_index);
+	resolve_undo_clear_index(the_repository->index);
 	return 0;
 }
 
@@ -888,7 +889,7 @@
 	*has_errors = do_unresolve(ctx->argc, ctx->argv,
 				prefix, prefix ? strlen(prefix) : 0);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -909,7 +910,7 @@
 	setup_work_tree();
 	*has_errors = do_reupdate(ctx->argv + 1, prefix);
 	if (*has_errors)
-		the_index.cache_changed = 0;
+		the_repository->index->cache_changed = 0;
 
 	ctx->argv += ctx->argc - 1;
 	ctx->argc = 1;
@@ -1056,7 +1057,7 @@
 	if (entries < 0)
 		die("cache corrupted");
 
-	the_index.updated_skipworktree = 1;
+	the_repository->index->updated_skipworktree = 1;
 
 	/*
 	 * Custom copy of parse_options() because we want to handle
@@ -1111,18 +1112,18 @@
 	getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf;
 	if (preferred_index_format) {
 		if (preferred_index_format < 0) {
-			printf(_("%d\n"), the_index.version);
+			printf(_("%d\n"), the_repository->index->version);
 		} else if (preferred_index_format < INDEX_FORMAT_LB ||
 			   INDEX_FORMAT_UB < preferred_index_format) {
 			die("index-version %d not in range: %d..%d",
 			    preferred_index_format,
 			    INDEX_FORMAT_LB, INDEX_FORMAT_UB);
 		} else {
-			if (the_index.version != preferred_index_format)
-				the_index.cache_changed |= SOMETHING_CHANGED;
+			if (the_repository->index->version != preferred_index_format)
+				the_repository->index->cache_changed |= SOMETHING_CHANGED;
 			report(_("index-version: was %d, set to %d"),
-			       the_index.version, preferred_index_format);
-			the_index.version = preferred_index_format;
+			       the_repository->index->version, preferred_index_format);
+			the_repository->index->version = preferred_index_format;
 		}
 	}
 
@@ -1159,16 +1160,16 @@
 			warning(_("core.splitIndex is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable split index"));
-		if (the_index.split_index)
-			the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+		if (the_repository->index->split_index)
+			the_repository->index->cache_changed |= SPLIT_INDEX_ORDERED;
 		else
-			add_split_index(&the_index);
+			add_split_index(the_repository->index);
 	} else if (!split_index) {
 		if (git_config_get_split_index() == 1)
 			warning(_("core.splitIndex is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable split index"));
-		remove_split_index(&the_index);
+		remove_split_index(the_repository->index);
 	}
 
 	prepare_repo_settings(r);
@@ -1180,7 +1181,7 @@
 			warning(_("core.untrackedCache is set to true; "
 				  "remove or change it, if you really want to "
 				  "disable the untracked cache"));
-		remove_untracked_cache(&the_index);
+		remove_untracked_cache(the_repository->index);
 		report(_("Untracked cache disabled"));
 		break;
 	case UC_TEST:
@@ -1192,7 +1193,7 @@
 			warning(_("core.untrackedCache is set to false; "
 				  "remove or change it, if you really want to "
 				  "enable the untracked cache"));
-		add_untracked_cache(&the_index);
+		add_untracked_cache(the_repository->index);
 		report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
 		break;
 	default:
@@ -1222,7 +1223,7 @@
 				"set it if you really want to "
 				"enable fsmonitor"));
 		}
-		add_fsmonitor(&the_index);
+		add_fsmonitor(the_repository->index);
 		report(_("fsmonitor enabled"));
 	} else if (!fsmonitor) {
 		enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
@@ -1230,17 +1231,17 @@
 			warning(_("core.fsmonitor is set; "
 				"remove it if you really want to "
 				"disable fsmonitor"));
-		remove_fsmonitor(&the_index);
+		remove_fsmonitor(the_repository->index);
 		report(_("fsmonitor disabled"));
 	}
 
-	if (the_index.cache_changed || force_write) {
+	if (the_repository->index->cache_changed || force_write) {
 		if (newfd < 0) {
 			if (refresh_args.flags & REFRESH_QUIET)
 				exit(128);
 			unable_to_lock_die(get_index_file(), lock_error);
 		}
-		if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+		if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e46afbc..6cda1c0 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -204,6 +204,7 @@
 
 	if (ref_transaction_update(transaction, refname,
 				   &new_oid, have_old ? &old_oid : NULL,
+				   NULL, NULL,
 				   update_flags | create_reflog_flag,
 				   msg, &err))
 		die("%s", err.buf);
@@ -397,7 +398,8 @@
 	struct ref_transaction *transaction;
 	int i, j;
 
-	transaction = ref_transaction_begin(&err);
+	transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+						  &err);
 	if (!transaction)
 		die("%s", err.buf);
 
@@ -464,7 +466,8 @@
 			 * get a "start".
 			 */
 			state = cmd->state;
-			transaction = ref_transaction_begin(&err);
+			transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+								  &err);
 			if (!transaction)
 				die("%s", err.buf);
 
@@ -571,11 +574,14 @@
 		 * For purposes of backwards compatibility, we treat
 		 * NULL_SHA1 as "don't care" here:
 		 */
-		return delete_ref(msg, refname,
-				  (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
-				  default_flags);
+		return refs_delete_ref(get_main_ref_store(the_repository),
+				       msg, refname,
+				       (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
+				       default_flags);
 	else
-		return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
-				  default_flags | create_reflog_flag,
-				  UPDATE_REFS_DIE_ON_ERR);
+		return refs_update_ref(get_main_ref_store(the_repository),
+				       msg, refname, &oid,
+				       oldval ? &oldoid : NULL,
+				       default_flags | create_reflog_flag,
+				       UPDATE_REFS_DIE_ON_ERR);
 }
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 15afb97..46d9327 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -9,6 +9,7 @@
 #include "upload-pack.h"
 #include "serve.h"
 #include "commit.h"
+#include "environment.h"
 
 static const char * const upload_pack_usage[] = {
 	N_("git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n"
@@ -39,6 +40,7 @@
 	packet_trace_identity("upload-pack");
 	disable_replace_refs();
 	save_commit_buffer = 0;
+	xsetenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 0);
 
 	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
diff --git a/builtin/var.c b/builtin/var.c
index cf55672..5dc3848 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -46,7 +46,7 @@
 
 static char *default_branch(int ident_flag UNUSED)
 {
-	return xstrdup_or_null(git_default_branch_name(1));
+	return repo_default_branch_name(the_repository, 1);
 }
 
 static char *shell_path(int ident_flag UNUSED)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 7c6c725..1d51e54 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -433,7 +433,7 @@
 
 	/* is 'refname' a branch or commit? */
 	if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
-	    ref_exists(symref.buf)) {
+	    refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
 		is_branch = 1;
 		if (!opts->force)
 			die_if_checked_out(symref.buf, 0);
@@ -509,7 +509,7 @@
 	}
 	wt_refs = get_worktree_ref_store(wt);
 
-	ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+	ret = ref_store_create_on_disk(wt_refs, REF_STORE_CREATE_ON_DISK_IS_WORKTREE, &sb);
 	if (ret)
 		goto done;
 
@@ -517,7 +517,7 @@
 		ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
 				      NULL, 0, UPDATE_REFS_MSG_ON_ERR);
 	else
-		ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+		ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL);
 	if (ret)
 		goto done;
 
@@ -605,7 +605,7 @@
 	} else {
 		struct strbuf s = STRBUF_INIT;
 		if (!detach && !strbuf_check_branch_ref(&s, branch) &&
-		    ref_exists(s.buf))
+		    refs_ref_exists(get_main_ref_store(the_repository), s.buf))
 			fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
 				  branch);
 		else {
@@ -647,9 +647,9 @@
  */
 static int can_use_local_refs(const struct add_opts *opts)
 {
-	if (head_ref(first_valid_ref, NULL)) {
+	if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		return 1;
-	} else if (for_each_branch_ref(first_valid_ref, NULL)) {
+	} else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		if (!opts->quiet) {
 			struct strbuf path = STRBUF_INIT;
 			struct strbuf contents = STRBUF_INIT;
@@ -689,7 +689,7 @@
 {
 	if (!guess_remote) {
 		return 0;
-	} else if (for_each_remote_ref(first_valid_ref, NULL)) {
+	} else if (refs_for_each_remote_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) {
 		return 1;
 	} else if (!opts->force && remote_get(NULL)) {
 		die(_("No local or remote refs exist despite at least one remote\n"
@@ -736,18 +736,17 @@
 	return 1;
 }
 
-static const char *dwim_branch(const char *path, const char **new_branch)
+static char *dwim_branch(const char *path, char **new_branch)
 {
 	int n;
 	int branch_exists;
 	const char *s = worktree_basename(path, &n);
-	const char *branchname = xstrndup(s, n);
+	char *branchname = xstrndup(s, n);
 	struct strbuf ref = STRBUF_INIT;
 
-	UNLEAK(branchname);
-
 	branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
-			ref_exists(ref.buf);
+			refs_ref_exists(get_main_ref_store(the_repository),
+					ref.buf);
 	strbuf_release(&ref);
 	if (branch_exists)
 		return branchname;
@@ -755,8 +754,7 @@
 	*new_branch = branchname;
 	if (guess_remote) {
 		struct object_id oid;
-		const char *remote =
-			unique_tracking_name(*new_branch, &oid, NULL);
+		char *remote = unique_tracking_name(*new_branch, &oid, NULL);
 		return remote;
 	}
 	return NULL;
@@ -768,6 +766,8 @@
 	const char *new_branch_force = NULL;
 	char *path;
 	const char *branch;
+	char *branch_to_free = NULL;
+	char *new_branch_to_free = NULL;
 	const char *new_branch = NULL;
 	const char *opt_track = NULL;
 	const char *lock_reason = NULL;
@@ -838,7 +838,7 @@
 
 		if (!opts.force &&
 		    !strbuf_check_branch_ref(&symref, new_branch) &&
-		    ref_exists(symref.buf))
+		    refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
 			die_if_checked_out(symref.buf, 0);
 		strbuf_release(&symref);
 	}
@@ -858,16 +858,17 @@
 		opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
 	} else if (ac < 2) {
 		/* DWIM: Guess branch name from path. */
-		const char *s = dwim_branch(path, &new_branch);
+		char *s = dwim_branch(path, &new_branch_to_free);
 		if (s)
-			branch = s;
+			branch = branch_to_free = s;
+		new_branch = new_branch_to_free;
 
 		/* DWIM: Infer --orphan when repo has no refs. */
 		opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
 	} else if (ac == 2) {
 		struct object_id oid;
 		struct commit *commit;
-		const char *remote;
+		char *remote;
 
 		commit = lookup_commit_reference_by_name(branch);
 		if (!commit) {
@@ -922,6 +923,8 @@
 
 	ret = add_worktree(path, branch, &opts);
 	free(path);
+	free(branch_to_free);
+	free(new_branch_to_free);
 	return ret;
 }
 
@@ -974,7 +977,9 @@
 		if (wt->is_detached)
 			strbuf_addstr(&sb, "(detached HEAD)");
 		else if (wt->head_ref) {
-			char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+			char *ref = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
+								 wt->head_ref,
+								 0);
 			strbuf_addf(&sb, "[%s]", ref);
 			free(ref);
 		} else
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 66e83d0..8c75b46 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#define USE_THE_INDEX_VARIABLE
+
 #include "builtin.h"
 #include "config.h"
 #include "environment.h"
@@ -44,8 +44,8 @@
 	prepare_repo_settings(the_repository);
 	the_repository->settings.command_requires_full_index = 0;
 
-	ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
-				  tree_prefix);
+	ret = write_index_as_tree(&oid, the_repository->index, get_index_file(),
+				  flags, tree_prefix);
 	switch (ret) {
 	case 0:
 		printf("%s\n", oid_to_hex(&oid));
diff --git a/bundle-uri.c b/bundle-uri.c
index ca32050..91b3319 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -395,11 +395,13 @@
 		strbuf_setlen(&bundle_ref, bundle_prefix_len);
 		strbuf_addstr(&bundle_ref, branch_name);
 
-		has_old = !read_ref(bundle_ref.buf, &old_oid);
-		update_ref("fetched bundle", bundle_ref.buf, oid,
-			   has_old ? &old_oid : NULL,
-			   REF_SKIP_OID_VERIFICATION,
-			   UPDATE_REFS_MSG_ON_ERR);
+		has_old = !refs_read_ref(get_main_ref_store(the_repository),
+					 bundle_ref.buf, &old_oid);
+		refs_update_ref(get_main_ref_store(the_repository),
+				"fetched bundle", bundle_ref.buf, oid,
+				has_old ? &old_oid : NULL,
+				REF_SKIP_OID_VERIFICATION,
+				UPDATE_REFS_MSG_ON_ERR);
 	}
 
 	bundle_header_release(&header);
diff --git a/bundle.c b/bundle.c
index a9744da..95367c2 100644
--- a/bundle.c
+++ b/bundle.c
@@ -389,7 +389,7 @@
 		if (repo_dwim_ref(the_repository, e->name, strlen(e->name),
 				  &oid, &ref, 0) != 1)
 			goto skip_write_ref;
-		if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
+		if (refs_read_ref_full(get_main_ref_store(the_repository), e->name, RESOLVE_REF_READING, &oid, &flag))
 			flag = 0;
 		display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
diff --git a/checkout.c b/checkout.c
index 4256e71..cfaea4b 100644
--- a/checkout.c
+++ b/checkout.c
@@ -45,8 +45,8 @@
 	return 0;
 }
 
-const char *unique_tracking_name(const char *name, struct object_id *oid,
-				 int *dwim_remotes_matched)
+char *unique_tracking_name(const char *name, struct object_id *oid,
+			   int *dwim_remotes_matched)
 {
 	struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
 	const char *default_remote = NULL;
diff --git a/checkout.h b/checkout.h
index 3c514a5..ba15a13 100644
--- a/checkout.h
+++ b/checkout.h
@@ -8,8 +8,8 @@
  * tracking branch.  Return the name of the remote if such a branch
  * exists, NULL otherwise.
  */
-const char *unique_tracking_name(const char *name,
-				 struct object_id *oid,
-				 int *dwim_remotes_matched);
+char *unique_tracking_name(const char *name,
+			   struct object_id *oid,
+			   int *dwim_remotes_matched);
 
 #endif /* CHECKOUT_H */
diff --git a/ci/check-whitespace.sh b/ci/check-whitespace.sh
new file mode 100755
index 0000000..db39909
--- /dev/null
+++ b/ci/check-whitespace.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# Check that commits after a specified point do not contain new or modified
+# lines with whitespace errors. An optional formatted summary can be generated
+# by providing an output file path and url as additional arguments.
+#
+
+baseCommit=$1
+outputFile=$2
+url=$3
+
+if test "$#" -ne 1 && test "$#" -ne 3
+then
+	echo "USAGE: $0 <BASE_COMMIT> [<OUTPUT_FILE> <URL>]"
+	exit 1
+fi
+
+problems=()
+commit=
+commitText=
+commitTextmd=
+goodParent=
+
+while read dash sha etc
+do
+	case "${dash}" in
+	"---") # Line contains commit information.
+		if test -z "${goodParent}"
+		then
+			# Assume the commit has no whitespace errors until detected otherwise.
+			goodParent=${sha}
+		fi
+
+		commit="${sha}"
+		commitText="${sha} ${etc}"
+		commitTextmd="[${sha}](${url}/commit/${sha}) ${etc}"
+		;;
+	"")
+		;;
+	*) # Line contains whitespace error information for current commit.
+		if test -n "${goodParent}"
+		then
+			problems+=("1) --- ${commitTextmd}")
+			echo ""
+			echo "--- ${commitText}"
+			goodParent=
+		fi
+
+		case "${dash}" in
+		*:[1-9]*:) # contains file and line number information
+			dashend=${dash#*:}
+			problems+=("[${dash}](${url}/blob/${commit}/${dash%%:*}#L${dashend%:}) ${sha} ${etc}")
+			;;
+		*)
+			problems+=("\`${dash} ${sha} ${etc}\`")
+			;;
+		esac
+		echo "${dash} ${sha} ${etc}"
+		;;
+	esac
+done <<< "$(git log --check --pretty=format:"---% h% s" "${baseCommit}"..)"
+
+if test ${#problems[*]} -gt 0
+then
+	if test -z "${goodParent}"
+	then
+		goodParent=${baseCommit: 0:7}
+	fi
+
+	echo "A whitespace issue was found in onen of more of the commits."
+	echo "Run the following command to resolve whitespace issues:"
+	echo "git rebase --whitespace=fix ${goodParent}"
+
+	# If target output file is provided, write formatted output.
+	if test -n "$outputFile"
+	then
+		echo "🛑 Please review the Summary output for further information."
+		(
+			echo "### :x: A whitespace issue was found in one or more of the commits."
+			echo ""
+			echo "Run these commands to correct the problem:"
+			echo "1. \`git rebase --whitespace=fix ${goodParent}\`"
+			echo "1. \`git push --force\`"
+			echo ""
+			echo "Errors:"
+
+			for i in "${problems[@]}"
+			do
+				echo "${i}"
+			done
+		) >"$outputFile"
+	fi
+
+	exit 2
+fi
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index b4e22de..6ec0f85 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -1,49 +1,81 @@
-#!/usr/bin/env bash
+#!/bin/sh
 #
 # Install dependencies required to build and test Git on Linux and macOS
 #
 
 . ${0%/*}/lib.sh
 
+begin_group "Install dependencies"
+
 P4WHENCE=https://cdist2.perforce.com/perforce/r21.2
 LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
-UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
- tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl
- libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
+JGITWHENCE=https://repo.eclipse.org/content/groups/releases//org/eclipse/jgit/org.eclipse.jgit.pgm/6.8.0.202311291450-r/org.eclipse.jgit.pgm-6.8.0.202311291450-r.sh
 
-case "$runs_on_pool" in
+# Make sudo a no-op and execute the command directly when running as root.
+# While using sudo would be fine on most platforms when we are root already,
+# some platforms like e.g. Alpine Linux do not have sudo available by default
+# and would thus break.
+if test "$(id -u)" -eq 0
+then
+	sudo () {
+		"$@"
+	}
+fi
+
+case "$distro" in
+alpine-*)
+	apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+		pcre2-dev python3 musl-libintl perl-utils ncurses \
+		apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+		bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null
+	;;
+fedora-*)
+	dnf -yq update >/dev/null &&
+	dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+	;;
 ubuntu-*)
+	# Required so that apt doesn't wait for user input on certain packages.
+	export DEBIAN_FRONTEND=noninteractive
+
 	sudo apt-get -q update
-	sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
-		$UBUNTU_COMMON_PKGS $CC_PACKAGE $PYTHON_PACKAGE
-	mkdir --parents "$P4_PATH"
-	pushd "$P4_PATH"
-		wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
-		wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
-		chmod u+x p4d
-		chmod u+x p4
-	popd
-	mkdir --parents "$GIT_LFS_PATH"
-	pushd "$GIT_LFS_PATH"
-		wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
-		tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
-		cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
-	popd
+	sudo apt-get -q -y install \
+		language-pack-is libsvn-perl apache2 cvs cvsps git gnupg subversion \
+		make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
+		tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
+		libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \
+		${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE
+
+	mkdir --parents "$CUSTOM_PATH"
+	wget --quiet --directory-prefix="$CUSTOM_PATH" \
+		"$P4WHENCE/bin.linux26x86_64/p4d" "$P4WHENCE/bin.linux26x86_64/p4"
+	chmod a+x "$CUSTOM_PATH/p4d" "$CUSTOM_PATH/p4"
+
+	wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+	tar -xzf "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" \
+		-C "$CUSTOM_PATH" --strip-components=1 "git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs"
+	rm "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+
+	wget --quiet "$JGITWHENCE" --output-document="$CUSTOM_PATH/jgit"
+	chmod a+x "$CUSTOM_PATH/jgit"
+	;;
+ubuntu32-*)
+	sudo linux32 --32bit i386 sh -c '
+		apt update >/dev/null &&
+		apt install -y build-essential libcurl4-openssl-dev \
+			libssl-dev libexpat-dev gettext python >/dev/null
+	'
 	;;
 macos-*)
 	export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
 	# Uncomment this if you want to run perf tests:
 	# brew install gnu-time
-	test -z "$BREW_INSTALL_PACKAGES" ||
-	brew install $BREW_INSTALL_PACKAGES
 	brew link --force gettext
 
-	mkdir -p "$P4_PATH"
-	pushd "$P4_PATH"
-		wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
-		tar -xf helix-core-server.tgz &&
-		sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
-	popd
+	mkdir -p "$CUSTOM_PATH"
+	wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
+	tar -xf helix-core-server.tgz -C "$CUSTOM_PATH" p4 p4d &&
+	sudo xattr -d com.apple.quarantine "$CUSTOM_PATH/p4" "$CUSTOM_PATH/p4d" 2>/dev/null || true
+	rm helix-core-server.tgz
 
 	if test -n "$CC_PACKAGE"
 	then
@@ -72,10 +104,6 @@
 	test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
 	sudo gem install --version 1.5.8 asciidoctor
 	;;
-linux-gcc-default)
-	sudo apt-get -q update
-	sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
-	;;
 esac
 
 if type p4d >/dev/null 2>&1 && type p4 >/dev/null 2>&1
@@ -87,6 +115,7 @@
 else
 	echo >&2 "WARNING: perforce wasn't installed, see above for clues why"
 fi
+
 if type git-lfs >/dev/null 2>&1
 then
 	echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)"
@@ -94,3 +123,13 @@
 else
 	echo >&2 "WARNING: git-lfs wasn't installed, see above for clues why"
 fi
+
+if type jgit >/dev/null 2>&1
+then
+	echo "$(tput setaf 6)JGit Version$(tput sgr0)"
+	jgit version
+else
+	echo >&2 "WARNING: JGit wasn't installed, see above for clues why"
+fi
+
+end_group "Install dependencies"
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
deleted file mode 100755
index eb2c9e1..0000000
--- a/ci/install-docker-dependencies.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-#
-# Install dependencies required to build and test Git inside container
-#
-
-. ${0%/*}/lib.sh
-
-begin_group "Install dependencies"
-
-case "$jobname" in
-linux32)
-	linux32 --32bit i386 sh -c '
-		apt update >/dev/null &&
-		apt install -y build-essential libcurl4-openssl-dev \
-			libssl-dev libexpat-dev gettext python >/dev/null
-	'
-	;;
-linux-musl)
-	apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
-		pcre2-dev python3 musl-libintl perl-utils ncurses \
-		apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
-		bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
-	;;
-linux-*|StaticAnalysis)
-	# Required so that apt doesn't wait for user input on certain packages.
-	export DEBIAN_FRONTEND=noninteractive
-
-	apt update -q &&
-	apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
-		libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
-		perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
-		libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
-		apache2 cvs cvsps gnupg libcgi-pm-perl subversion
-
-	if test "$jobname" = StaticAnalysis
-	then
-		apt install -q -y coccinelle
-	fi
-	;;
-pedantic)
-	dnf -yq update >/dev/null &&
-	dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
-	;;
-esac
-
-end_group "Install dependencies"
diff --git a/ci/lib.sh b/ci/lib.sh
index 0a73fc7..814578f 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -18,7 +18,7 @@
 then
 	begin_group () {
 		need_to_end_group=t
-		printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+		printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)[collapsed=true]\r\e[0K$1\n"
 		trap "end_group '$1'" EXIT
 		set -x
 	}
@@ -279,7 +279,7 @@
 
 	cache_dir="$HOME/none"
 
-	runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+	distro=$(echo "$CI_JOB_IMAGE" | tr : -)
 	JOBS=$(nproc)
 else
 	echo "Could not identify CI type" >&2
@@ -318,16 +318,20 @@
 export GIT_TEST_CLONE_2GB=true
 export SKIP_DASHED_BUILT_INS=YesPlease
 
-case "$runs_on_pool" in
+case "$distro" in
 ubuntu-*)
 	if test "$jobname" = "linux-gcc-default"
 	then
 		break
 	fi
 
-	PYTHON_PACKAGE=python2
-	if test "$jobname" = linux-gcc
+	# Python 2 is end of life, and Ubuntu 23.04 and newer don't actually
+	# have it anymore. We thus only test with Python 2 on older LTS
+	# releases.
+	if test "$distro" = "ubuntu-20.04"
 	then
+		PYTHON_PACKAGE=python2
+	else
 		PYTHON_PACKAGE=python3
 	fi
 	MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/$PYTHON_PACKAGE"
@@ -340,10 +344,6 @@
 	# image.
 	# Keep that in mind when you encounter a broken OS X build!
 	export LINUX_GIT_LFS_VERSION="1.5.2"
-
-	P4_PATH="$HOME/custom/p4"
-	GIT_LFS_PATH="$HOME/custom/git-lfs"
-	export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
 	;;
 macos-*)
 	MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
@@ -351,12 +351,12 @@
 	then
 		MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
 	fi
-
-	P4_PATH="$HOME/custom/p4"
-	export PATH="$P4_PATH:$PATH"
 	;;
 esac
 
+CUSTOM_PATH="${CUSTOM_PATH:-$HOME/path}"
+export PATH="$CUSTOM_PATH:$PATH"
+
 case "$jobname" in
 linux32)
 	CC=gcc
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
index a51076d..797d65c 100755
--- a/ci/run-build-and-minimal-fuzzers.sh
+++ b/ci/run-build-and-minimal-fuzzers.sh
@@ -7,7 +7,7 @@
 
 group "Build fuzzers" make \
 	CC=clang \
-	CXX=clang++ \
+	FUZZ_CXX=clang++ \
 	CFLAGS="-fsanitize=fuzzer-no-link,address" \
 	LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
 	fuzz-all
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index c192bd6..98dda42 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -53,8 +53,6 @@
 then
 	group "Run tests" make test ||
 	handle_failed_tests
-	group "Run unit tests" \
-		make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
 fi
 check_unignored_build_artifacts
 
diff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh
index ae80943..e167e64 100755
--- a/ci/run-test-slice.sh
+++ b/ci/run-test-slice.sh
@@ -17,7 +17,7 @@
 
 # We only have one unit test at the moment, so run it in the first slice
 if [ "$1" == "0" ] ; then
-	group "Run unit tests" make --quiet -C t unit-tests-prove
+	group "Run unit tests" make --quiet -C t unit-tests-test-tool
 fi
 
 check_unignored_build_artifacts
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index de41888..02b3af3 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -11,6 +11,7 @@
 	    -e '/^    \* new asciidoc flags$/d' \
 	    -e '/stripped namespace before processing/d' \
 	    -e '/Attributed.*IDs for element/d' \
+	    -e '/SyntaxWarning: invalid escape sequence/d' \
 	    "$1"
 }
 
diff --git a/color.c b/color.c
index f663c06..227a5ab 100644
--- a/color.c
+++ b/color.c
@@ -64,12 +64,16 @@
 	return !strncasecmp(word, match, len) && !match[len];
 }
 
-static int get_hex_color(const char *in, unsigned char *out)
+static int get_hex_color(const char **inp, int width, unsigned char *out)
 {
+	const char *in = *inp;
 	unsigned int val;
-	val = (hexval(in[0]) << 4) | hexval(in[1]);
+
+	assert(width == 1 || width == 2);
+	val = (hexval(in[0]) << 4) | hexval(in[width - 1]);
 	if (val & ~0xff)
 		return -1;
+	*inp += width;
 	*out = val;
 	return 0;
 }
@@ -135,11 +139,14 @@
 		return 0;
 	}
 
-	/* Try a 24-bit RGB value */
-	if (len == 7 && name[0] == '#') {
-		if (!get_hex_color(name + 1, &out->red) &&
-		    !get_hex_color(name + 3, &out->green) &&
-		    !get_hex_color(name + 5, &out->blue)) {
+	/* Try a 24- or 12-bit RGB value prefixed with '#' */
+	if ((len == 7 || len == 4) && name[0] == '#') {
+		int width_per_color = (len == 7) ? 2 : 1;
+		const char *color = name + 1;
+
+		if (!get_hex_color(&color, width_per_color, &out->red) &&
+		    !get_hex_color(&color, width_per_color, &out->green) &&
+		    !get_hex_color(&color, width_per_color, &out->blue)) {
 			out->type = COLOR_RGB;
 			return 0;
 		}
diff --git a/color.h b/color.h
index bb28343..7ed259a 100644
--- a/color.h
+++ b/color.h
@@ -112,7 +112,8 @@
  * Translate a Git color from 'value' into a string that the terminal can
  * interpret and store it into 'dst'. The Git color values are of the form
  * "foreground [background] [attr]" where fore- and background can be a color
- * name ("red"), a RGB code (#0xFF0000) or a 256-color-mode from the terminal.
+ * name ("red"), a RGB code (#FF0000 or #F00) or a 256-color-mode from the
+ * terminal.
  */
 int color_parse(const char *value, char *dst);
 int color_parse_mem(const char *value, int len, char *dst);
diff --git a/combine-diff.c b/combine-diff.c
index d6d6fa1..4960d90 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1066,7 +1066,8 @@
 			elem->mode = canon_mode(st.st_mode);
 		} else if (S_ISDIR(st.st_mode)) {
 			struct object_id oid;
-			if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
+			if (repo_resolve_gitlink_ref(the_repository, elem->path,
+						     "HEAD", &oid) < 0)
 				result = grab_blob(opt->repo, &elem->oid,
 						   elem->mode, &result_size,
 						   NULL, NULL);
diff --git a/command-list.txt b/command-list.txt
index c4cd0f3..e0bb87b 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -157,6 +157,7 @@
 git-rebase                              mainporcelain           history
 git-receive-pack                        synchelpers
 git-reflog                              ancillarymanipulators           complete
+git-refs                                ancillarymanipulators           complete
 git-remote                              ancillarymanipulators           complete
 git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
diff --git a/commit-graph.c b/commit-graph.c
index 45417d7..e5dd355 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1821,7 +1821,7 @@
 	struct object_id peeled;
 	struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
 
-	if (!peel_iterated_oid(oid, &peeled))
+	if (!peel_iterated_oid(the_repository, oid, &peeled))
 		oid = &peeled;
 	if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
 		oidset_insert(data->commits, oid);
@@ -1845,7 +1845,8 @@
 		data.progress = start_delayed_progress(
 			_("Collecting referenced commits"), 0);
 
-	for_each_ref(add_ref_to_set, &data);
+	refs_for_each_ref(get_main_ref_store(the_repository), add_ref_to_set,
+			  &data);
 
 	stop_progress(&data.progress);
 
diff --git a/commit-reach.c b/commit-reach.c
index 8f9b008..384aee1 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -1106,6 +1106,10 @@
 
 	/* STALE is used here, PARENT2 is used by insert_no_dup(). */
 	repo_clear_commit_marks(r, PARENT2 | STALE);
+	while (prio_queue_peek(&queue)) {
+		struct commit *c = prio_queue_get(&queue);
+		free_bit_array(c);
+	}
 	clear_bit_arrays(&bit_arrays);
 	clear_prio_queue(&queue);
 }
diff --git a/commit.c b/commit.c
index 1a479a9..1d08951 100644
--- a/commit.c
+++ b/commit.c
@@ -1070,7 +1070,8 @@
 
 	memset(&revs, 0, sizeof(revs));
 	revs.initial = 1;
-	for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+				 full_refname, collect_one_reflog_ent, &revs);
 
 	if (!revs.nr)
 		add_one_commit(&oid, &revs);
diff --git a/common-main.c b/common-main.c
index 033778b..b86f406 100644
--- a/common-main.c
+++ b/common-main.c
@@ -48,7 +48,7 @@
 	setlocale(LC_CTYPE, "");
 	git_setup_gettext();
 
-	initialize_the_repository();
+	initialize_repository(the_repository);
 
 	attr_start();
 
diff --git a/compat/mingw.c b/compat/mingw.c
index 4876344..6b06ea5 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -3159,6 +3159,7 @@
 	return 0;
 }
 
+#ifndef NO_UNIX_SOCKETS
 int mingw_have_unix_sockets(void)
 {
 	SC_HANDLE scm, srvc;
@@ -3177,3 +3178,4 @@
 	}
 	return ret;
 }
+#endif
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index d1bc09e..2bc0f11 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -868,7 +868,7 @@
     if (table_size > pat_len)
       break;
 
-  dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+  dfa->state_table = calloc (table_size, sizeof (struct re_state_table_entry));
   dfa->state_hash_mask = table_size - 1;
 
   dfa->mb_cur_max = MB_CUR_MAX;
@@ -936,7 +936,7 @@
 	{
 	  int i, j, ch;
 
-	  dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+	  dfa->sb_char = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
 	  if (BE (dfa->sb_char == NULL, 0))
 	    return REG_ESPACE;
 
@@ -3079,9 +3079,9 @@
 						   _NL_COLLATE_SYMB_EXTRAMB);
     }
 #endif
-  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+  sbcset = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
 #ifdef RE_ENABLE_I18N
-  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+  mbcset = (re_charset_t *) calloc (1, sizeof (re_charset_t));
 #endif /* RE_ENABLE_I18N */
 #ifdef RE_ENABLE_I18N
   if (BE (sbcset == NULL || mbcset == NULL, 0))
@@ -3626,9 +3626,9 @@
   re_token_t br_token;
   bin_tree_t *tree;
 
-  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+  sbcset = (re_bitset_ptr_t) calloc (1, sizeof (bitset_t));
 #ifdef RE_ENABLE_I18N
-  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+  mbcset = (re_charset_t *) calloc (1, sizeof (re_charset_t));
 #endif /* RE_ENABLE_I18N */
 
 #ifdef RE_ENABLE_I18N
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
index ec51cf3..ec5cc5d 100644
--- a/compat/regex/regex_internal.c
+++ b/compat/regex/regex_internal.c
@@ -1628,7 +1628,7 @@
   reg_errcode_t err;
   re_dfastate_t *newstate;
 
-  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  newstate = (re_dfastate_t *) calloc (1, sizeof (re_dfastate_t));
   if (BE (newstate == NULL, 0))
     return NULL;
   err = re_node_set_init_copy (&newstate->nodes, nodes);
@@ -1678,7 +1678,7 @@
   reg_errcode_t err;
   re_dfastate_t *newstate;
 
-  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  newstate = (re_dfastate_t *) calloc (1, sizeof (re_dfastate_t));
   if (BE (newstate == NULL, 0))
     return NULL;
   err = re_node_set_init_copy (&newstate->nodes, nodes);
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index 49358ae..e92be57 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -2796,8 +2796,8 @@
 	    continue; /* No.  */
 	  if (sub_top->path == NULL)
 	    {
-	      sub_top->path = calloc (sizeof (state_array_t),
-				      sl_str - sub_top->str_idx + 1);
+	      sub_top->path = calloc (sl_str - sub_top->str_idx + 1,
+				      sizeof (state_array_t));
 	      if (sub_top->path == NULL)
 		return REG_ESPACE;
 	    }
@@ -3361,7 +3361,7 @@
       if (ndests == 0)
 	{
 	  state->trtable = (re_dfastate_t **)
-	    calloc (sizeof (re_dfastate_t *), SBC_MAX);
+	    calloc (SBC_MAX, sizeof (re_dfastate_t *));
 	  return 1;
 	}
       return 0;
@@ -3457,7 +3457,7 @@
 	 discern by looking at the character code: allocate a
 	 256-entry transition table.  */
       trtable = state->trtable =
-	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+	(re_dfastate_t **) calloc (SBC_MAX, sizeof (re_dfastate_t *));
       if (BE (trtable == NULL, 0))
 	goto out_free;
 
@@ -3488,7 +3488,7 @@
 	 transition tables, one starting at trtable[0] and one
 	 starting at trtable[SBC_MAX].  */
       trtable = state->word_trtable =
-	(re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+	(re_dfastate_t **) calloc (2 * SBC_MAX, sizeof (re_dfastate_t *));
       if (BE (trtable == NULL, 0))
 	goto out_free;
 
diff --git a/config.c b/config.c
index 2766b14..ea355f2 100644
--- a/config.c
+++ b/config.c
@@ -125,7 +125,7 @@
 	config_fn_t fn;
 	void *data;
 	const struct config_options *opts;
-	struct git_config_source *config_source;
+	const struct git_config_source *config_source;
 	struct repository *repo;
 
 	/*
@@ -303,7 +303,8 @@
 	int ret;
 	struct strbuf pattern = STRBUF_INIT;
 	const char *refname = !the_repository->gitdir ?
-		NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+		NULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+					       "HEAD", 0, NULL, &flags);
 	const char *shortname;
 
 	if (!refname || !(flags & REF_ISSYMREF)	||
@@ -1337,7 +1338,7 @@
 	return v;
 }
 
-int git_config_string(const char **dest, const char *var, const char *value)
+int git_config_string(char **dest, const char *var, const char *value)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -1345,7 +1346,7 @@
 	return 0;
 }
 
-int git_config_pathname(const char **dest, const char *var, const char *value)
+int git_config_pathname(char **dest, const char *var, const char *value)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -1413,11 +1414,15 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.attributesfile"))
+	if (!strcmp(var, "core.attributesfile")) {
+		FREE_AND_NULL(git_attributes_file);
 		return git_config_pathname(&git_attributes_file, var, value);
+	}
 
-	if (!strcmp(var, "core.hookspath"))
+	if (!strcmp(var, "core.hookspath")) {
+		FREE_AND_NULL(git_hooks_path);
 		return git_config_pathname(&git_hooks_path, var, value);
+	}
 
 	if (!strcmp(var, "core.bare")) {
 		is_bare_repository_cfg = git_config_bool(var, value);
@@ -1552,8 +1557,10 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.checkroundtripencoding"))
+	if (!strcmp(var, "core.checkroundtripencoding")) {
+		FREE_AND_NULL(check_roundtrip_encoding);
 		return git_config_string(&check_roundtrip_encoding, var, value);
+	}
 
 	if (!strcmp(var, "core.notesref")) {
 		if (!value)
@@ -1562,8 +1569,10 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.editor"))
+	if (!strcmp(var, "core.editor")) {
+		FREE_AND_NULL(editor_program);
 		return git_config_string(&editor_program, var, value);
+	}
 
 	if (!strcmp(var, "core.commentchar") ||
 	    !strcmp(var, "core.commentstring")) {
@@ -1581,11 +1590,13 @@
 		return 0;
 	}
 
-	if (!strcmp(var, "core.askpass"))
+	if (!strcmp(var, "core.askpass")) {
+		FREE_AND_NULL(askpass_program);
 		return git_config_string(&askpass_program, var, value);
+	}
 
 	if (!strcmp(var, "core.excludesfile")) {
-		free((char *)excludes_file);
+		FREE_AND_NULL(excludes_file);
 		return git_config_pathname(&excludes_file, var, value);
 	}
 
@@ -1688,11 +1699,15 @@
 
 static int git_default_i18n_config(const char *var, const char *value)
 {
-	if (!strcmp(var, "i18n.commitencoding"))
+	if (!strcmp(var, "i18n.commitencoding")) {
+		FREE_AND_NULL(git_commit_encoding);
 		return git_config_string(&git_commit_encoding, var, value);
+	}
 
-	if (!strcmp(var, "i18n.logoutputencoding"))
+	if (!strcmp(var, "i18n.logoutputencoding")) {
+		FREE_AND_NULL(git_log_output_encoding);
 		return git_config_string(&git_log_output_encoding, var, value);
+	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
@@ -1765,10 +1780,15 @@
 
 static int git_default_mailmap_config(const char *var, const char *value)
 {
-	if (!strcmp(var, "mailmap.file"))
+	if (!strcmp(var, "mailmap.file")) {
+		FREE_AND_NULL(git_mailmap_file);
 		return git_config_pathname(&git_mailmap_file, var, value);
-	if (!strcmp(var, "mailmap.blob"))
+	}
+
+	if (!strcmp(var, "mailmap.blob")) {
+		FREE_AND_NULL(git_mailmap_blob);
 		return git_config_string(&git_mailmap_blob, var, value);
+	}
 
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
@@ -1776,8 +1796,10 @@
 
 static int git_default_attr_config(const char *var, const char *value)
 {
-	if (!strcmp(var, "attr.tree"))
+	if (!strcmp(var, "attr.tree")) {
+		FREE_AND_NULL(git_attr_tree);
 		return git_config_string(&git_attr_tree, var, value);
+	}
 
 	/*
 	 * Add other attribute related config variables here and to
@@ -2105,7 +2127,7 @@
 }
 
 int config_with_options(config_fn_t fn, void *data,
-			struct git_config_source *config_source,
+			const struct git_config_source *config_source,
 			struct repository *repo,
 			const struct config_options *opts)
 {
@@ -2404,7 +2426,7 @@
 {
 	const char *value;
 	if (!git_configset_get_value(set, key, &value, NULL))
-		return git_config_string((const char **)dest, key, value);
+		return git_config_string(dest, key, value);
 	else
 		return 1;
 }
@@ -2482,7 +2504,7 @@
 		return 1;
 }
 
-int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, char **dest)
 {
 	const char *value;
 	if (!git_configset_get_value(set, key, &value, NULL))
@@ -2627,7 +2649,7 @@
 }
 
 int repo_config_get_pathname(struct repository *repo,
-			     const char *key, const char **dest)
+			     const char *key, char **dest)
 {
 	int ret;
 	git_config_check_init(repo);
@@ -2726,7 +2748,7 @@
 	return repo_config_get_maybe_bool(the_repository, key, dest);
 }
 
-int git_config_get_pathname(const char *key, const char **dest)
+int git_config_get_pathname(const char *key, char **dest)
 {
 	return repo_config_get_pathname(the_repository, key, dest);
 }
@@ -3181,14 +3203,10 @@
 	trace2_cmd_set_config(key, value);
 }
 
-/*
- * The ownership rule is that the caller will own the string
- * if it receives a piece of memory different from what it passed
- * as the parameter.
- */
-const char *git_config_prepare_comment_string(const char *comment)
+char *git_config_prepare_comment_string(const char *comment)
 {
 	size_t leading_blanks;
+	char *prepared;
 
 	if (!comment)
 		return NULL;
@@ -3209,13 +3227,13 @@
 
 	leading_blanks = strspn(comment, " \t");
 	if (leading_blanks && comment[leading_blanks] == '#')
-		; /* use it as-is */
+		prepared = xstrdup(comment); /* use it as-is */
 	else if (comment[0] == '#')
-		comment = xstrfmt(" %s", comment);
+		prepared = xstrfmt(" %s", comment);
 	else
-		comment = xstrfmt(" # %s", comment);
+		prepared = xstrfmt(" # %s", comment);
 
-	return comment;
+	return prepared;
 }
 
 static void validate_comment_string(const char *comment)
diff --git a/config.h b/config.h
index f4966e3..8620aa7 100644
--- a/config.h
+++ b/config.h
@@ -232,7 +232,7 @@
  * sets `opts.respect_includes` to `1` by default.
  */
 int config_with_options(config_fn_t fn, void *,
-			struct git_config_source *config_source,
+			const struct git_config_source *config_source,
 			struct repository *repo,
 			const struct config_options *opts);
 
@@ -280,13 +280,13 @@
  * Allocates and copies the value string into the `dest` parameter; if no
  * string is given, prints an error message and returns -1.
  */
-int git_config_string(const char **, const char *, const char *);
+int git_config_string(char **, const char *, const char *);
 
 /**
  * Similar to `git_config_string`, but expands `~` or `~user` into the
  * user's home directory when found at the beginning of the path.
  */
-int git_config_pathname(const char **, const char *, const char *);
+int git_config_pathname(char **, const char *, const char *);
 
 int git_config_expiry_date(timestamp_t *, const char *, const char *);
 int git_config_color(char *, const char *, const char *);
@@ -338,7 +338,7 @@
 int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
 int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, const char *, unsigned);
 
-const char *git_config_prepare_comment_string(const char *);
+char *git_config_prepare_comment_string(const char *);
 
 /**
  * takes four parameters:
@@ -541,7 +541,7 @@
 int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
 int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
 int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
+int git_configset_get_pathname(struct config_set *cs, const char *key, char **dest);
 
 /* Functions for reading a repository's config */
 struct repository;
@@ -577,7 +577,7 @@
 int repo_config_get_maybe_bool(struct repository *repo,
 			       const char *key, int *dest);
 int repo_config_get_pathname(struct repository *repo,
-			     const char *key, const char **dest);
+			     const char *key, char **dest);
 
 /*
  * Functions for reading protected config. By definition, protected
@@ -687,7 +687,7 @@
  * Similar to `git_config_get_string`, but expands `~` or `~user` into
  * the user's home directory when found at the beginning of the path.
  */
-int git_config_get_pathname(const char *key, const char **dest);
+int git_config_get_pathname(const char *key, char **dest);
 
 int git_config_get_index_threads(int *dest);
 int git_config_get_split_index(void);
diff --git a/config.mak.uname b/config.mak.uname
index a7607a5..85d6382 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -68,6 +68,7 @@
         ifneq ($(findstring .el7.,$(uname_R)),)
 		BASIC_CFLAGS += -std=c99
         endif
+	LINK_FUZZ_PROGRAMS = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	HAVE_ALLOCA_H = YesPlease
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 804629c..2f9c335 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1005,10 +1005,11 @@
 
 #test-tool
 parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
 
 list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
 add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES})
-target_link_libraries(test-tool common-main)
+target_link_libraries(test-tool test-lib common-main)
 
 set_target_properties(test-fake-ssh test-tool
 			PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/helper)
diff --git a/contrib/coccinelle/refs.cocci b/contrib/coccinelle/refs.cocci
new file mode 100644
index 0000000..31d9cad
--- /dev/null
+++ b/contrib/coccinelle/refs.cocci
@@ -0,0 +1,103 @@
+// Migrate "refs.h" to not rely on `the_repository` implicitly anymore.
+@@
+@@
+(
+- resolve_ref_unsafe
++ refs_resolve_ref_unsafe
+|
+- resolve_refdup
++ refs_resolve_refdup
+|
+- read_ref_full
++ refs_read_ref_full
+|
+- read_ref
++ refs_read_ref
+|
+- ref_exists
++ refs_ref_exists
+|
+- head_ref
++ refs_head_ref
+|
+- for_each_ref
++ refs_for_each_ref
+|
+- for_each_ref_in
++ refs_for_each_ref_in
+|
+- for_each_fullref_in
++ refs_for_each_fullref_in
+|
+- for_each_tag_ref
++ refs_for_each_tag_ref
+|
+- for_each_branch_ref
++ refs_for_each_branch_ref
+|
+- for_each_remote_ref
++ refs_for_each_remote_ref
+|
+- for_each_glob_ref
++ refs_for_each_glob_ref
+|
+- for_each_glob_ref_in
++ refs_for_each_glob_ref_in
+|
+- head_ref_namespaced
++ refs_head_ref_namespaced
+|
+- for_each_namespaced_ref
++ refs_for_each_namespaced_ref
+|
+- for_each_rawref
++ refs_for_each_rawref
+|
+- safe_create_reflog
++ refs_create_reflog
+|
+- reflog_exists
++ refs_reflog_exists
+|
+- delete_ref
++ refs_delete_ref
+|
+- delete_refs
++ refs_delete_refs
+|
+- delete_reflog
++ refs_delete_reflog
+|
+- for_each_reflog_ent
++ refs_for_each_reflog_ent
+|
+- for_each_reflog_ent_reverse
++ refs_for_each_reflog_ent_reverse
+|
+- for_each_reflog
++ refs_for_each_reflog
+|
+- shorten_unambiguous_ref
++ refs_shorten_unambiguous_ref
+|
+- rename_ref
++ refs_rename_ref
+|
+- copy_existing_ref
++ refs_copy_existing_ref
+|
+- create_symref
++ refs_create_symref
+|
+- ref_transaction_begin
++ ref_store_transaction_begin
+|
+- update_ref
++ refs_update_ref
+|
+- reflog_expire
++ refs_reflog_expire
+)
+  (
++ get_main_ref_store(the_repository),
+  ...)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 75193de..60a22d6 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -31,15 +31,29 @@
 # Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
 # just like the 'git commit' command.
 #
-# If you have a command that is not part of git, but you would still
-# like completion, you can use __git_complete:
+# To add completion for git subcommands that are implemented in external
+# scripts, define a function of the form '_git_${subcommand}' while replacing
+# all dashes with underscores, and the main git completion will make use of it.
+# For example, to add completion for 'git do-stuff' (which could e.g. live
+# in /usr/bin/git-do-stuff), name the completion function '_git_do_stuff'.
+# See _git_show, _git_bisect etc. below for more examples.
+#
+# If you have a shell command that is not part of git (and is not called as a
+# git subcommand), but you would still like git-style completion for it, use
+# __git_complete. For example, to use the same completion as for 'git log' also
+# for the 'gl' command:
 #
 #   __git_complete gl git_log
 #
-# Or if it's a main command (i.e. git or gitk):
+# Or if the 'gk' command should be completed the same as 'gitk':
 #
 #   __git_complete gk gitk
 #
+# The second parameter of __git_complete gives the completion function; it is
+# resolved as a function named "$2", or "__$2_main", or "_$2" in that order.
+# In the examples above, the actual functions used for completion will be
+# _git_log and __gitk_main.
+#
 # Compatible with bash 3.2.57.
 #
 # You can set the following environment variables to influence the behavior of
@@ -2975,22 +2989,42 @@
 
 _git_config ()
 {
-	case "$prev" in
-	--get|--get-all|--unset|--unset-all)
-		__gitcomp_nl "$(__git_config_get_set_variables)"
+	local subcommands subcommand
+
+	__git_resolve_builtins "config"
+
+	subcommands="$___git_resolved_builtins"
+	subcommand="$(__git_find_subcommand "$subcommands")"
+
+	if [ -z "$subcommand" ]
+	then
+		__gitcomp "$subcommands"
 		return
-		;;
-	*.*)
-		__git_complete_config_variable_value
+	fi
+
+	case "$cur" in
+	--*)
+		__gitcomp_builtin "config_$subcommand"
 		return
 		;;
 	esac
-	case "$cur" in
-	--*)
-		__gitcomp_builtin config
+
+	case "$subcommand" in
+	get)
+		__gitcomp_nl "$(__git_config_get_set_variables)"
 		;;
-	*)
-		__git_complete_config_variable_name
+	set)
+		case "$prev" in
+		*.*)
+			__git_complete_config_variable_value
+			;;
+		*)
+			__git_complete_config_variable_name
+			;;
+		esac
+		;;
+	unset)
+		__gitcomp_nl "$(__git_config_get_set_variables)"
 		;;
 	esac
 }
@@ -3581,6 +3615,17 @@
 	fi
 }
 
+_git_symbolic_ref () {
+	case "$cur" in
+	--*)
+		__gitcomp_builtin symbolic-ref
+		return
+		;;
+	esac
+
+	__git_complete_refs
+}
+
 _git_tag ()
 {
 	local i c="$__git_cmd_idx" f=0
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index cac6f61..f5877bd 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -272,6 +272,7 @@
 {
 	local _ret=1
 	local cur cword prev
+	local __git_repo_path
 
 	cur=${words[CURRENT]}
 	prev=${words[CURRENT-1]}
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 6a40917..6ce22a2 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -12,6 +12,7 @@
 static CFDataRef password;
 static CFDataRef password_expiry_utc;
 static CFDataRef oauth_refresh_token;
+static int state_seen;
 
 static void clear_credential(void)
 {
@@ -171,6 +172,9 @@
 
 	CFRelease(item);
 
+	write_item("capability[]", "state", strlen("state"));
+	write_item("state[]", "osxkeychain:seen=1", strlen("osxkeychain:seen=1"));
+
 out:
 	CFRelease(attrs);
 
@@ -284,6 +288,9 @@
 	CFDictionaryRef attrs;
 	OSStatus result;
 
+	if (state_seen)
+		return errSecSuccess;
+
 	/* Only store complete credentials */
 	if (!protocol || !host || !username || !password)
 		return -1;
@@ -395,6 +402,10 @@
 			oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
 							   (UInt8 *)v,
 							   strlen(v));
+		else if (!strcmp(buf, "state[]")) {
+			if (!strcmp(v, "osxkeychain:seen=1"))
+				state_seen = 1;
+		}
 		/*
 		 * Ignore other lines; we don't know what they mean, but
 		 * this future-proofs us when later versions of git do
@@ -414,6 +425,9 @@
 	if (!argv[1])
 		die("%s", usage);
 
+	if (open(argv[0], O_RDONLY | O_EXLOCK) == -1)
+		die("failed to lock %s", argv[0]);
+
 	read_credential();
 
 	if (!strcmp(argv[1], "get"))
diff --git a/convert.c b/convert.c
index 35b25eb..f2b9f01 100644
--- a/convert.c
+++ b/convert.c
@@ -345,30 +345,32 @@
 	 * space separated encodings (eg. "UTF-16, ASCII, CP1125").
 	 * Search for the given encoding in that string.
 	 */
-	const char *found = strcasestr(check_roundtrip_encoding, enc_name);
+	const char *encoding = check_roundtrip_encoding ?
+		check_roundtrip_encoding : "SHIFT-JIS";
+	const char *found = strcasestr(encoding, enc_name);
 	const char *next;
 	int len;
 	if (!found)
 		return 0;
 	next = found + strlen(enc_name);
-	len = strlen(check_roundtrip_encoding);
+	len = strlen(encoding);
 	return (found && (
 			/*
-			 * check that the found encoding is at the
-			 * beginning of check_roundtrip_encoding or
-			 * that it is prefixed with a space or comma
+			 * Check that the found encoding is at the beginning of
+			 * encoding or that it is prefixed with a space or
+			 * comma.
 			 */
-			found == check_roundtrip_encoding || (
+			found == encoding || (
 				(isspace(found[-1]) || found[-1] == ',')
 			)
 		) && (
 			/*
-			 * check that the found encoding is at the
-			 * end of check_roundtrip_encoding or
-			 * that it is suffixed with a space or comma
+			 * Check that the found encoding is at the end of
+			 * encoding or that it is suffixed with a space
+			 * or comma.
 			 */
-			next == check_roundtrip_encoding + len || (
-				next < check_roundtrip_encoding + len &&
+			next == encoding + len || (
+				next < encoding + len &&
 				(isspace(next[0]) || next[0] == ',')
 			)
 		));
@@ -979,9 +981,9 @@
 static struct convert_driver {
 	const char *name;
 	struct convert_driver *next;
-	const char *smudge;
-	const char *clean;
-	const char *process;
+	char *smudge;
+	char *clean;
+	char *process;
 	int required;
 } *user_convert, **user_convert_tail;
 
diff --git a/convert.h b/convert.h
index ab8b4fa..d925589 100644
--- a/convert.h
+++ b/convert.h
@@ -92,7 +92,7 @@
 		   struct conv_attrs *ca, const char *path);
 
 extern enum eol core_eol;
-extern const char *check_roundtrip_encoding;
+extern char *check_roundtrip_encoding;
 const char *get_cached_convert_stats_ascii(struct index_state *istate,
 					   const char *path);
 const char *get_wt_convert_stats_ascii(const char *path);
diff --git a/copy.c b/copy.c
index 23d84c6..d9d2092 100644
--- a/copy.c
+++ b/copy.c
@@ -1,6 +1,9 @@
 #include "git-compat-util.h"
 #include "copy.h"
 #include "path.h"
+#include "gettext.h"
+#include "strbuf.h"
+#include "abspath.h"
 
 int copy_fd(int ifd, int ofd)
 {
diff --git a/credential.c b/credential.c
index 18098bd..4b1a2b9 100644
--- a/credential.c
+++ b/credential.c
@@ -20,18 +20,68 @@
 
 void credential_clear(struct credential *c)
 {
+	credential_clear_secrets(c);
 	free(c->protocol);
 	free(c->host);
 	free(c->path);
 	free(c->username);
-	free(c->password);
 	free(c->oauth_refresh_token);
+	free(c->authtype);
 	string_list_clear(&c->helpers, 0);
 	strvec_clear(&c->wwwauth_headers);
+	strvec_clear(&c->state_headers);
+	strvec_clear(&c->state_headers_to_send);
 
 	credential_init(c);
 }
 
+void credential_next_state(struct credential *c)
+{
+	strvec_clear(&c->state_headers_to_send);
+	SWAP(c->state_headers, c->state_headers_to_send);
+}
+
+void credential_clear_secrets(struct credential *c)
+{
+	FREE_AND_NULL(c->password);
+	FREE_AND_NULL(c->credential);
+}
+
+static void credential_set_capability(struct credential_capability *capa,
+				      enum credential_op_type op_type)
+{
+	switch (op_type) {
+	case CREDENTIAL_OP_INITIAL:
+		capa->request_initial = 1;
+		break;
+	case CREDENTIAL_OP_HELPER:
+		capa->request_helper = 1;
+		break;
+	case CREDENTIAL_OP_RESPONSE:
+		capa->response = 1;
+		break;
+	}
+}
+
+
+void credential_set_all_capabilities(struct credential *c,
+				     enum credential_op_type op_type)
+{
+	credential_set_capability(&c->capa_authtype, op_type);
+	credential_set_capability(&c->capa_state, op_type);
+}
+
+static void announce_one(struct credential_capability *cc, const char *name, FILE *fp) {
+	if (cc->request_initial)
+		fprintf(fp, "capability %s\n", name);
+}
+
+void credential_announce_capabilities(struct credential *c, FILE *fp) {
+	fprintf(fp, "version 0\n");
+	announce_one(&c->capa_authtype, "authtype", fp);
+	announce_one(&c->capa_state, "state", fp);
+}
+
 int credential_match(const struct credential *want,
 		     const struct credential *have, int match_password)
 {
@@ -40,7 +90,8 @@
 	       CHECK(host) &&
 	       CHECK(path) &&
 	       CHECK(username) &&
-	       (!match_password || CHECK(password));
+	       (!match_password || CHECK(password)) &&
+	       (!match_password || CHECK(credential));
 #undef CHECK
 }
 
@@ -208,7 +259,26 @@
 						 PROMPT_ASKPASS);
 }
 
-int credential_read(struct credential *c, FILE *fp)
+int credential_has_capability(const struct credential_capability *capa,
+			      enum credential_op_type op_type)
+{
+	/*
+	 * We're checking here if each previous step indicated that we had the
+	 * capability.  If it did, then we want to pass it along; conversely, if
+	 * it did not, we don't want to report that to our caller.
+	 */
+	switch (op_type) {
+	case CREDENTIAL_OP_HELPER:
+		return capa->request_initial;
+	case CREDENTIAL_OP_RESPONSE:
+		return capa->request_initial && capa->request_helper;
+	default:
+		return 0;
+	}
+}
+
+int credential_read(struct credential *c, FILE *fp,
+		    enum credential_op_type op_type)
 {
 	struct strbuf line = STRBUF_INIT;
 
@@ -233,6 +303,9 @@
 		} else if (!strcmp(key, "password")) {
 			free(c->password);
 			c->password = xstrdup(value);
+		} else if (!strcmp(key, "credential")) {
+			free(c->credential);
+			c->credential = xstrdup(value);
 		} else if (!strcmp(key, "protocol")) {
 			free(c->protocol);
 			c->protocol = xstrdup(value);
@@ -242,8 +315,19 @@
 		} else if (!strcmp(key, "path")) {
 			free(c->path);
 			c->path = xstrdup(value);
+		} else if (!strcmp(key, "ephemeral")) {
+			c->ephemeral = !!git_config_bool("ephemeral", value);
 		} else if (!strcmp(key, "wwwauth[]")) {
 			strvec_push(&c->wwwauth_headers, value);
+		} else if (!strcmp(key, "state[]")) {
+			strvec_push(&c->state_headers, value);
+		} else if (!strcmp(key, "capability[]")) {
+			if (!strcmp(value, "authtype"))
+				credential_set_capability(&c->capa_authtype, op_type);
+			else if (!strcmp(value, "state"))
+				credential_set_capability(&c->capa_state, op_type);
+		} else if (!strcmp(key, "continue")) {
+			c->multistage = !!git_config_bool("continue", value);
 		} else if (!strcmp(key, "password_expiry_utc")) {
 			errno = 0;
 			c->password_expiry_utc = parse_timestamp(value, NULL, 10);
@@ -252,6 +336,9 @@
 		} else if (!strcmp(key, "oauth_refresh_token")) {
 			free(c->oauth_refresh_token);
 			c->oauth_refresh_token = xstrdup(value);
+		} else if (!strcmp(key, "authtype")) {
+			free(c->authtype);
+			c->authtype = xstrdup(value);
 		} else if (!strcmp(key, "url")) {
 			credential_from_url(c, value);
 		} else if (!strcmp(key, "quit")) {
@@ -280,8 +367,20 @@
 	fprintf(fp, "%s=%s\n", key, value);
 }
 
-void credential_write(const struct credential *c, FILE *fp)
+void credential_write(const struct credential *c, FILE *fp,
+		      enum credential_op_type op_type)
 {
+	if (credential_has_capability(&c->capa_authtype, op_type))
+		credential_write_item(fp, "capability[]", "authtype", 0);
+	if (credential_has_capability(&c->capa_state, op_type))
+		credential_write_item(fp, "capability[]", "state", 0);
+
+	if (credential_has_capability(&c->capa_authtype, op_type)) {
+		credential_write_item(fp, "authtype", c->authtype, 0);
+		credential_write_item(fp, "credential", c->credential, 0);
+		if (c->ephemeral)
+			credential_write_item(fp, "ephemeral", "1", 0);
+	}
 	credential_write_item(fp, "protocol", c->protocol, 1);
 	credential_write_item(fp, "host", c->host, 1);
 	credential_write_item(fp, "path", c->path, 0);
@@ -295,6 +394,12 @@
 	}
 	for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
 		credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i], 0);
+	if (credential_has_capability(&c->capa_state, op_type)) {
+		if (c->multistage)
+			credential_write_item(fp, "continue", "1", 0);
+		for (size_t i = 0; i < c->state_headers_to_send.nr; i++)
+			credential_write_item(fp, "state[]", c->state_headers_to_send.v[i], 0);
+	}
 }
 
 static int run_credential_helper(struct credential *c,
@@ -317,14 +422,14 @@
 
 	fp = xfdopen(helper.in, "w");
 	sigchain_push(SIGPIPE, SIG_IGN);
-	credential_write(c, fp);
+	credential_write(c, fp, want_output ? CREDENTIAL_OP_HELPER : CREDENTIAL_OP_RESPONSE);
 	fclose(fp);
 	sigchain_pop(SIGPIPE);
 
 	if (want_output) {
 		int r;
 		fp = xfdopen(helper.out, "r");
-		r = credential_read(c, fp);
+		r = credential_read(c, fp, CREDENTIAL_OP_HELPER);
 		fclose(fp);
 		if (r < 0) {
 			finish_command(&helper);
@@ -357,32 +462,45 @@
 	return r;
 }
 
-void credential_fill(struct credential *c)
+void credential_fill(struct credential *c, int all_capabilities)
 {
 	int i;
 
-	if (c->username && c->password)
+	if ((c->username && c->password) || c->credential)
 		return;
 
+	credential_next_state(c);
+	c->multistage = 0;
+
 	credential_apply_config(c);
+	if (all_capabilities)
+		credential_set_all_capabilities(c, CREDENTIAL_OP_INITIAL);
 
 	for (i = 0; i < c->helpers.nr; i++) {
 		credential_do(c, c->helpers.items[i].string, "get");
+
 		if (c->password_expiry_utc < time(NULL)) {
-			/* Discard expired password */
-			FREE_AND_NULL(c->password);
+			/*
+			 * Don't use credential_clear() here: callers such as
+			 * cmd_credential() expect to still be able to call
+			 * credential_write() on a struct credential whose
+			 * secrets have expired.
+			 */
+			credential_clear_secrets(c);
 			/* Reset expiry to maintain consistency */
 			c->password_expiry_utc = TIME_MAX;
 		}
-		if (c->username && c->password)
+		if ((c->username && c->password) || c->credential) {
+			strvec_clear(&c->wwwauth_headers);
 			return;
+		}
 		if (c->quit)
 			die("credential helper '%s' told us to quit",
 			    c->helpers.items[i].string);
 	}
 
 	credential_getpass(c);
-	if (!c->username && !c->password)
+	if (!c->username && !c->password && !c->credential)
 		die("unable to get password from user");
 }
 
@@ -392,9 +510,11 @@
 
 	if (c->approved)
 		return;
-	if (!c->username || !c->password || c->password_expiry_utc < time(NULL))
+	if (((!c->username || !c->password) && !c->credential) || c->password_expiry_utc < time(NULL))
 		return;
 
+	credential_next_state(c);
+
 	credential_apply_config(c);
 
 	for (i = 0; i < c->helpers.nr; i++)
@@ -406,13 +526,15 @@
 {
 	int i;
 
+	credential_next_state(c);
+
 	credential_apply_config(c);
 
 	for (i = 0; i < c->helpers.nr; i++)
 		credential_do(c, c->helpers.items[i].string, "erase");
 
+	credential_clear_secrets(c);
 	FREE_AND_NULL(c->username);
-	FREE_AND_NULL(c->password);
 	FREE_AND_NULL(c->oauth_refresh_token);
 	c->password_expiry_utc = TIME_MAX;
 	c->approved = 0;
diff --git a/credential.h b/credential.h
index acc41ad..5f9e6ff 100644
--- a/credential.h
+++ b/credential.h
@@ -5,8 +5,8 @@
 #include "strvec.h"
 
 /**
- * The credentials API provides an abstracted way of gathering username and
- * password credentials from the user.
+ * The credentials API provides an abstracted way of gathering
+ * authentication credentials from the user.
  *
  * Typical setup
  * -------------
@@ -93,13 +93,35 @@
  * -----------------------------------------------------------------------
  */
 
+/*
+ * These values define the kind of operation we're performing and the
+ * capabilities at each stage.  The first is either an external request (via git
+ * credential fill) or an internal request (e.g., via the HTTP) code.  The
+ * second is the call to the credential helper, and the third is the response
+ * we're providing.
+ *
+ * At each stage, we will emit the capability only if the previous stage
+ * supported it.
+ */
+enum credential_op_type {
+	CREDENTIAL_OP_INITIAL  = 1,
+	CREDENTIAL_OP_HELPER   = 2,
+	CREDENTIAL_OP_RESPONSE = 3,
+};
+
+struct credential_capability {
+	unsigned request_initial:1,
+		 request_helper:1,
+		 response:1;
+};
 
 /**
- * This struct represents a single username/password combination
- * along with any associated context. All string fields should be
- * heap-allocated (or NULL if they are not known or not applicable).
- * The meaning of the individual context fields is the same as
- * their counterparts in the helper protocol.
+ * This struct represents a single login credential (typically a
+ * username/password combination) along with any associated
+ * context. All string fields should be heap-allocated (or NULL if
+ * they are not known or not applicable). The meaning of the
+ * individual context fields is the same as their counterparts in
+ * the helper protocol.
  *
  * This struct should always be initialized with `CREDENTIAL_INIT` or
  * `credential_init`.
@@ -124,6 +146,16 @@
 	struct strvec wwwauth_headers;
 
 	/**
+	 * A `strvec` of state headers received from credential helpers.
+	 */
+	struct strvec state_headers;
+
+	/**
+	 * A `strvec` of state headers to send to credential helpers.
+	 */
+	struct strvec state_headers_to_send;
+
+	/**
 	 * Internal use only. Keeps track of if we previously matched against a
 	 * WWW-Authenticate header line in order to re-fold future continuation
 	 * lines into one value.
@@ -131,24 +163,38 @@
 	unsigned header_is_last_match:1;
 
 	unsigned approved:1,
+		 ephemeral:1,
 		 configured:1,
+		 multistage: 1,
 		 quit:1,
 		 use_http_path:1,
 		 username_from_proto:1;
 
+	struct credential_capability capa_authtype;
+	struct credential_capability capa_state;
+
 	char *username;
 	char *password;
+	char *credential;
 	char *protocol;
 	char *host;
 	char *path;
 	char *oauth_refresh_token;
 	timestamp_t password_expiry_utc;
+
+	/**
+	 * The authorization scheme to use.  If this is NULL, libcurl is free to
+	 * negotiate any scheme it likes.
+	 */
+	char *authtype;
 };
 
 #define CREDENTIAL_INIT { \
 	.helpers = STRING_LIST_INIT_DUP, \
 	.password_expiry_utc = TIME_MAX, \
 	.wwwauth_headers = STRVEC_INIT, \
+	.state_headers = STRVEC_INIT, \
+	.state_headers_to_send = STRVEC_INIT, \
 }
 
 /* Initialize a credential structure, setting all fields to empty. */
@@ -162,13 +208,17 @@
 
 /**
  * Instruct the credential subsystem to fill the username and
- * password fields of the passed credential struct by first
- * consulting helpers, then asking the user. After this function
- * returns, the username and password fields of the credential are
- * guaranteed to be non-NULL. If an error occurs, the function will
- * die().
+ * password (or authtype and credential) fields of the passed
+ * credential struct by first consulting helpers, then asking the
+ * user. After this function returns, either the username and
+ * password fields or the credential field of the credential are
+ * guaranteed to be non-NULL. If an error occurs, the function
+ * will die().
+ *
+ * If all_capabilities is set, this is an internal user that is prepared
+ * to deal with all known capabilities, and we should advertise that fact.
  */
-void credential_fill(struct credential *);
+void credential_fill(struct credential *, int all_capabilities);
 
 /**
  * Inform the credential subsystem that the provided credentials
@@ -184,15 +234,53 @@
  * have been rejected. This will cause the credential subsystem to
  * notify any helpers of the rejection (which allows them, for
  * example, to purge the invalid credentials from storage). It
- * will also free() the username and password fields of the
- * credential and set them to NULL (readying the credential for
- * another call to `credential_fill`). Any errors from helpers are
- * ignored.
+ * will also free() the username, password, and credential fields
+ * of the credential and set them to NULL (readying the credential
+ * for another call to `credential_fill`). Any errors from helpers
+ * are ignored.
  */
 void credential_reject(struct credential *);
 
-int credential_read(struct credential *, FILE *);
-void credential_write(const struct credential *, FILE *);
+/**
+ * Enable all of the supported credential flags in this credential.
+ */
+void credential_set_all_capabilities(struct credential *c,
+				     enum credential_op_type op_type);
+
+/**
+ * Clear the secrets in this credential, but leave other data intact.
+ *
+ * This is useful for resetting credentials in preparation for a subsequent
+ * stage of filling.
+ */
+void credential_clear_secrets(struct credential